From 24e5aaca90ddd4830667e377fcb1892e838548c3 Mon Sep 17 00:00:00 2001 From: Fiyaz Bin Hasan Date: Sun, 14 Aug 2022 15:59:43 +0600 Subject: [PATCH 01/18] work --- .../minimal-apis/7.0-samples/todo-group/Program.cs | 12 ++++++------ .../{RouteHandlers.cs => TodoEndpoints.cs} | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/{RouteHandlers.cs => TodoEndpoints.cs} (98%) diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs index adc1fbfb8d87..48f6d579e2bd 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs @@ -25,14 +25,14 @@ app.MapGet("/", () => "Hello World!"); // todo endpoints -var todos = app.MapGroup("/todos").WithTags("Todo Endpoints").AddRouteHandlerFilter(async (context, next) => +var todos = app.MapGroup("/todos").WithTags("Todo Endpoints").AddEndpointFilter(async (context, next) => { app.Logger.LogInformation("Accessing todo endpoints"); return await next(context); }); -todos.MapGet("/", RouteHandlers.GetAllTodos); -todos.MapGet("/{id}", RouteHandlers.GetTodo); -todos.MapPost("/", RouteHandlers.CreateTodo).AddRouteHandlerFilter(async (context, next) => +todos.MapGet("/", TodoEndpoints.GetAllTodos); +todos.MapGet("/{id}", TodoEndpoints.GetTodo); +todos.MapPost("/", TodoEndpoints.CreateTodo).AddEndpointFilter(async (context, next) => { // log time taken to process var start = DateTime.Now; @@ -41,7 +41,7 @@ app.Logger.LogInformation($"{context.HttpContext.Request.Path.Value} took {(end - start).TotalMilliseconds}ms"); return result; }); -todos.MapPut("/{id}", RouteHandlers.UpdateTodo).AddRouteHandlerFilter(async (context, next) => +todos.MapPut("/{id}", TodoEndpoints.UpdateTodo).AddEndpointFilter(async (context, next) => { // log time taken to process var start = DateTime.Now; @@ -50,6 +50,6 @@ app.Logger.LogInformation($"{context.HttpContext.Request.Path.Value} took {(end - start).TotalMilliseconds}ms"); return result; }); -todos.MapDelete("/{id}", RouteHandlers.DeleteTodo); +todos.MapDelete("/{id}", TodoEndpoints.DeleteTodo); app.Run(); diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/RouteHandlers.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpoints.cs similarity index 98% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/RouteHandlers.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpoints.cs index cb2334ccfdb7..615fdd0e0fbc 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/RouteHandlers.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpoints.cs @@ -1,7 +1,7 @@ using Data; using Microsoft.EntityFrameworkCore; -public class RouteHandlers +public class TodoEndpoints { // get all todos public static async Task GetAllTodos(TodoGroupDbContext database) From be92d7727c652ef77d0d36298b968e1e6bdcb39f Mon Sep 17 00:00:00 2001 From: Fiyaz Bin Hasan Date: Sun, 14 Aug 2022 22:58:05 +0600 Subject: [PATCH 02/18] test sample --- .../MinApiTests/MinApiTests.csproj | 28 ++++++++++++ .../7.0-samples/MinApiTests/TodoTests.cs | 43 +++++++++++++++++++ .../7.0-samples/MinApiTests/Usings.cs | 1 + .../7.0-samples/todo-group/Program.cs | 1 + .../7.0-samples/todo-group/TodoEndpoints.cs | 18 ++++---- 5 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoTests.cs create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/Usings.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj new file mode 100644 index 000000000000..d5a3f3901d4f --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj @@ -0,0 +1,28 @@ + + + + net7.0 + enable + enable + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoTests.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoTests.cs new file mode 100644 index 000000000000..933486798c08 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoTests.cs @@ -0,0 +1,43 @@ +using todo_group; +using Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.Data.Sqlite; +using Microsoft.AspNetCore.Http.HttpResults; + +namespace MinApiTests; + +public class TodoTests +{ + [Fact] + public async Task GetTodoReturnsTodoFromDatabase() + { + var todo = new Todo { Id = 42, Title = "Improve Results testability!" }; + + await using var mockDb = new MockTodoDb().CreateDbContext(); + await mockDb.Todos.AddAsync(todo); + await mockDb.SaveChangesAsync(); + + var result = (Ok)await TodoEndpoints.GetTodo(todo.Id, mockDb); + //Assert + Assert.Equal(200, result.StatusCode); + + var foundTodo = Assert.IsAssignableFrom(result); + Assert.Equal(42, foundTodo.Id); + } +} + +public class MockTodoDb : IDbContextFactory +{ + public TodoGroupDbContext CreateDbContext() + { + var connection = new SqliteConnection("DataSource=:memory:"); + connection.Open(); + + var options = new DbContextOptionsBuilder() + .UseSqlite(connection) + .Options; + + var dbContext = new TodoGroupDbContext(options); + return dbContext; + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/Usings.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/Usings.cs new file mode 100644 index 000000000000..8c927eb747a6 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/Usings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs index 48f6d579e2bd..4fa99ac12f6a 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs @@ -1,6 +1,7 @@ using Data; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; +using todo_group; var builder = WebApplication.CreateBuilder(args); var connection = new SqliteConnection("DataSource=:memory:"); diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpoints.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpoints.cs index 615fdd0e0fbc..6eb832b1f69a 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpoints.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpoints.cs @@ -1,13 +1,15 @@ using Data; using Microsoft.EntityFrameworkCore; +namespace todo_group; + public class TodoEndpoints { // get all todos public static async Task GetAllTodos(TodoGroupDbContext database) { var todos = await database.Todos.ToListAsync(); - return TypedResults.Ok(todos); + return Results.Ok(todos); } // get todo by id @@ -16,9 +18,9 @@ public static async Task GetTodo(int id, TodoGroupDbContext database) var todo = await database.Todos.FindAsync(id); if (todo != null) { - return TypedResults.Ok(todo); + return Results.Ok(todo); } - return TypedResults.NotFound(); + return Results.NotFound(); } // create todo @@ -32,7 +34,7 @@ public static async Task CreateTodo(TodoDto todo, TodoGroupDbContext da }; await database.Todos.AddAsync(newTodo); await database.SaveChangesAsync(); - return TypedResults.Created($"/public/todos/{newTodo.Id}", newTodo); + return Results.Created($"/public/todos/{newTodo.Id}", newTodo); } // update todo @@ -45,10 +47,10 @@ public static async Task UpdateTodo(Todo todo, TodoGroupDbContext datab existingTodo.Description = todo.Description; existingTodo.IsDone = todo.IsDone; await database.SaveChangesAsync(); - return TypedResults.Created($"/public/todos/{existingTodo.Id}", existingTodo); + return Results.Created($"/public/todos/{existingTodo.Id}", existingTodo); } - return TypedResults.NotFound(); + return Results.NotFound(); } // delete todo @@ -59,8 +61,8 @@ public static async Task DeleteTodo(int id, TodoGroupDbContext database { database.Todos.Remove(todo); await database.SaveChangesAsync(); - return TypedResults.NoContent(); + return Results.NoContent(); } - return TypedResults.NotFound(); + return Results.NotFound(); } } From cab37d0bff8999998bb43b50e2ddcca266ac7ce1 Mon Sep 17 00:00:00 2001 From: Fiyaz Bin Hasan Date: Sat, 20 Aug 2022 18:50:31 +0600 Subject: [PATCH 03/18] work --- .../MinApiTests/MinApiTests.csproj | 1 + .../7.0-samples/MinApiTests/TodoTests.cs | 77 +++++++++++++++---- .../7.0-samples/todo-group/Program.cs | 35 ++++++--- .../7.0-samples/todo-group/TodoEndpoints.cs | 20 +++-- .../7.0-samples/todo-group/Utilities.cs | 24 ++++++ 5 files changed, 122 insertions(+), 35 deletions(-) create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Utilities.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj index d5a3f3901d4f..d1c47240a5b5 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj @@ -9,6 +9,7 @@ + diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoTests.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoTests.cs index 933486798c08..d2e82fbee834 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoTests.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoTests.cs @@ -6,38 +6,85 @@ namespace MinApiTests; -public class TodoTests +public class TodoTests : IClassFixture { + private TodoGroupDbContext _context; + + public TodoTests(MockTodoDb mockTodo) + { + _context = mockTodo.CreateDbContext(); + } + [Fact] public async Task GetTodoReturnsTodoFromDatabase() { - var todo = new Todo { Id = 42, Title = "Improve Results testability!" }; + // Arrange + var todo = new Todo { Id = 42, Title = "Improve Results testability!", IsDone = false }; - await using var mockDb = new MockTodoDb().CreateDbContext(); - await mockDb.Todos.AddAsync(todo); - await mockDb.SaveChangesAsync(); + await _context.Todos.AddAsync(todo); + await _context.SaveChangesAsync(); - var result = (Ok)await TodoEndpoints.GetTodo(todo.Id, mockDb); - //Assert - Assert.Equal(200, result.StatusCode); + // Act + var okResult = (Ok) await TodoEndpoints.GetTodo(todo.Id, _context); - var foundTodo = Assert.IsAssignableFrom(result); + //Assert + Assert.Equal(200, okResult.StatusCode); + var foundTodo = Assert.IsAssignableFrom(okResult.Value); Assert.Equal(42, foundTodo.Id); } + + [Fact] + public async Task CreateTodoCreatesTodoInDatabase() + { + //Arrange + var newTodo = new TodoDto + { + Title = "Test title", + Description = "Test description", + IsDone = false + }; + + //Act + var createdResult = (Created) await TodoEndpoints.CreateTodo(newTodo, _context); + + //Assert + Assert.Equal(201, createdResult.StatusCode); + Assert.NotNull(createdResult.Location); + Assert.IsAssignableFrom(createdResult.Value); + } + + [Fact] + public async Task UpdateTodoUpdatesTodoInDatabase() + { + //Arrange + var todo = new Todo { Id = 1, Title = "Test title", IsDone = false }; + + await _context.Todos.AddAsync(todo); + await _context.SaveChangesAsync(); + + _context.ChangeTracker.Clear(); + + //Act + var createdResult = (Created) await TodoEndpoints.UpdateTodo(new Todo() { Id = 1, IsDone = true }, _context); + var notFoundResult = (NotFound) await TodoEndpoints.UpdateTodo(new Todo() { Id = 2 }, _context); + + //Assert + Assert.Equal(201, createdResult.StatusCode); + Assert.NotNull(createdResult.Location); + Assert.IsAssignableFrom(createdResult.Value); + + Assert.Equal(400, notFoundResult.StatusCode); + } } public class MockTodoDb : IDbContextFactory { public TodoGroupDbContext CreateDbContext() { - var connection = new SqliteConnection("DataSource=:memory:"); - connection.Open(); - var options = new DbContextOptionsBuilder() - .UseSqlite(connection) + .UseInMemoryDatabase("TodoUnitTestsDb") .Options; - var dbContext = new TodoGroupDbContext(options); - return dbContext; + return new TodoGroupDbContext(options); } } diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs index 4fa99ac12f6a..099891ce23fd 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs @@ -31,17 +31,34 @@ app.Logger.LogInformation("Accessing todo endpoints"); return await next(context); }); + todos.MapGet("/", TodoEndpoints.GetAllTodos); todos.MapGet("/{id}", TodoEndpoints.GetTodo); -todos.MapPost("/", TodoEndpoints.CreateTodo).AddEndpointFilter(async (context, next) => -{ - // log time taken to process - var start = DateTime.Now; - var result = await next(context); - var end = DateTime.Now; - app.Logger.LogInformation($"{context.HttpContext.Request.Path.Value} took {(end - start).TotalMilliseconds}ms"); - return result; -}); + +todos.MapPost("/", TodoEndpoints.CreateTodo) + .AddEndpointFilter(async (context, next) => + { + // log time taken to process + var start = DateTime.Now; + var result = await next(context); + var end = DateTime.Now; + app.Logger.LogInformation($"{context.HttpContext.Request.Path.Value} took {(end - start).TotalMilliseconds}ms"); + return result; + }) + .AddEndpointFilter(async (efiContext, next) => + { + var tdparam = efiContext.GetArgument(0); + + var validationErrors = Utilities.IsValid(tdparam); + + if (validationErrors.Any()) + { + return Results.ValidationProblem(validationErrors); + } + + return await next(efiContext); + }); + todos.MapPut("/{id}", TodoEndpoints.UpdateTodo).AddEndpointFilter(async (context, next) => { // log time taken to process diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpoints.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpoints.cs index 6eb832b1f69a..0dadb763fe6a 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpoints.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpoints.cs @@ -9,7 +9,7 @@ public class TodoEndpoints public static async Task GetAllTodos(TodoGroupDbContext database) { var todos = await database.Todos.ToListAsync(); - return Results.Ok(todos); + return TypedResults.Ok(todos); } // get todo by id @@ -18,9 +18,9 @@ public static async Task GetTodo(int id, TodoGroupDbContext database) var todo = await database.Todos.FindAsync(id); if (todo != null) { - return Results.Ok(todo); + return TypedResults.Ok(todo); } - return Results.NotFound(); + return TypedResults.NotFound(); } // create todo @@ -34,7 +34,7 @@ public static async Task CreateTodo(TodoDto todo, TodoGroupDbContext da }; await database.Todos.AddAsync(newTodo); await database.SaveChangesAsync(); - return Results.Created($"/public/todos/{newTodo.Id}", newTodo); + return TypedResults.Created($"/public/todos/{newTodo.Id}", newTodo); } // update todo @@ -43,14 +43,12 @@ public static async Task UpdateTodo(Todo todo, TodoGroupDbContext datab var existingTodo = await database.Todos.FindAsync(todo.Id); if (existingTodo != null) { - existingTodo.Title = todo.Title; - existingTodo.Description = todo.Description; - existingTodo.IsDone = todo.IsDone; + database.Update(todo); await database.SaveChangesAsync(); - return Results.Created($"/public/todos/{existingTodo.Id}", existingTodo); + return TypedResults.Created($"/public/todos/{existingTodo.Id}", existingTodo); } - return Results.NotFound(); + return TypedResults.NotFound(); } // delete todo @@ -61,8 +59,8 @@ public static async Task DeleteTodo(int id, TodoGroupDbContext database { database.Todos.Remove(todo); await database.SaveChangesAsync(); - return Results.NoContent(); + return TypedResults.NoContent(); } - return Results.NotFound(); + return TypedResults.NotFound(); } } diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Utilities.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Utilities.cs new file mode 100644 index 000000000000..d981e9b3ce93 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Utilities.cs @@ -0,0 +1,24 @@ +using Data; + +namespace todo_group +{ + public static class Utilities + { + public static Dictionary IsValid(TodoDto td) + { + Dictionary errors = new(); + + if(string.IsNullOrEmpty(td.Title)) + { + errors.Add("todo.name.errors", new[] { "Name is empty" }); + } + + if (td.Title!.Length < 3) + { + errors.Add("todo.name.errors", new[] { "Name length < 3" }); + } + + return errors; + } + } +} From a572e8d823487d63b0aaad105d9dfa7cc40d378d Mon Sep 17 00:00:00 2001 From: Fiyaz Bin Hasan Date: Sun, 21 Aug 2022 11:56:46 +0600 Subject: [PATCH 04/18] work --- .../MinApiTests/MinApiTests.csproj | 1 + .../7.0-samples/MinApiTests/TodoTests.cs | 118 +++++++++++++----- .../7.0-samples/todo-group/Program.cs | 3 + .../todo-group/Services/ITodoService.cs | 13 ++ .../todo-group/Services/TodoService.cs | 43 +++++++ .../7.0-samples/todo-group/TodoEndpoints.cs | 41 +++--- 6 files changed, 169 insertions(+), 50 deletions(-) create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/ITodoService.cs create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/TodoService.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj index d1c47240a5b5..b1c9ddc1b90f 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj @@ -11,6 +11,7 @@ + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoTests.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoTests.cs index d2e82fbee834..f6c9773984be 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoTests.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoTests.cs @@ -1,36 +1,39 @@ using todo_group; using Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.Data.Sqlite; using Microsoft.AspNetCore.Http.HttpResults; +using Moq; +using todo_group.Services; +using System.Runtime.CompilerServices; namespace MinApiTests; -public class TodoTests : IClassFixture +public class TodoTests { - private TodoGroupDbContext _context; - - public TodoTests(MockTodoDb mockTodo) - { - _context = mockTodo.CreateDbContext(); - } - [Fact] public async Task GetTodoReturnsTodoFromDatabase() { // Arrange - var todo = new Todo { Id = 42, Title = "Improve Results testability!", IsDone = false }; + var mock = new Mock(); + + mock.Setup(m => m.Find(It.Is(id => id == 1))).ReturnsAsync(new Todo + { + Id = 1, + Title = "Test title", + IsDone = false + }); - await _context.Todos.AddAsync(todo); - await _context.SaveChangesAsync(); + mock.Setup(m => m.Find(It.Is(id => id == 2))).ReturnsAsync((Todo?)null); // Act - var okResult = (Ok) await TodoEndpoints.GetTodo(todo.Id, _context); + var okResult = (Ok)await TodoEndpoints.GetTodo(1, mock.Object); + var notFoundResult = (NotFound)await TodoEndpoints.GetTodo(404, mock.Object); //Assert Assert.Equal(200, okResult.StatusCode); var foundTodo = Assert.IsAssignableFrom(okResult.Value); - Assert.Equal(42, foundTodo.Id); + Assert.Equal(1, foundTodo.Id); + + Assert.Equal(404, notFoundResult.StatusCode); } [Fact] @@ -44,10 +47,15 @@ public async Task CreateTodoCreatesTodoInDatabase() IsDone = false }; + var mock = new Mock(); + + mock.Setup(m => m.Add(It.Is(t => t.Title == newTodo.Title && t.Description == newTodo.Description && t.IsDone == newTodo.IsDone))) + .Returns(Task.CompletedTask); + //Act - var createdResult = (Created) await TodoEndpoints.CreateTodo(newTodo, _context); + var createdResult = (Created)await TodoEndpoints.CreateTodo(newTodo, mock.Object); - //Assert + //Assert Assert.Equal(201, createdResult.StatusCode); Assert.NotNull(createdResult.Location); Assert.IsAssignableFrom(createdResult.Value); @@ -55,36 +63,78 @@ public async Task CreateTodoCreatesTodoInDatabase() [Fact] public async Task UpdateTodoUpdatesTodoInDatabase() - { + { //Arrange - var todo = new Todo { Id = 1, Title = "Test title", IsDone = false }; + var existingTodo = new Todo() + { + Id = 1, + Title = "Exiting test title", + IsDone = false + }; + + var updatedTodo = new Todo() + { + Id = 1, + Title = "Updated test title", + IsDone = true + }; + + var mock = new Mock(); - await _context.Todos.AddAsync(todo); - await _context.SaveChangesAsync(); + mock.Setup(m => m.Find(It.Is(id => id == 1))).ReturnsAsync(existingTodo); - _context.ChangeTracker.Clear(); + mock.Setup(m => m.Find(It.Is(id => id == 2))).ReturnsAsync((Todo?)null); + + mock.Setup(m => m.Update(It.Is(t => t.Id == updatedTodo.Id && t.Description == updatedTodo.Description && t.IsDone == updatedTodo.IsDone))) + .Callback((todo) => existingTodo = todo) + .Returns(Task.CompletedTask); //Act - var createdResult = (Created) await TodoEndpoints.UpdateTodo(new Todo() { Id = 1, IsDone = true }, _context); - var notFoundResult = (NotFound) await TodoEndpoints.UpdateTodo(new Todo() { Id = 2 }, _context); + var createdResult = (Created)await TodoEndpoints.UpdateTodo(updatedTodo, mock.Object); + var notFoundResult = (NotFound)await TodoEndpoints.UpdateTodo(new Todo() { Id = 2, Title = "Invalid Title" }, mock.Object); - //Assert + //Assert Assert.Equal(201, createdResult.StatusCode); Assert.NotNull(createdResult.Location); Assert.IsAssignableFrom(createdResult.Value); - Assert.Equal(400, notFoundResult.StatusCode); + Assert.Equal("Updated test title", existingTodo.Title); + Assert.True(existingTodo.IsDone); + + Assert.Equal(404, notFoundResult.StatusCode); } -} -public class MockTodoDb : IDbContextFactory -{ - public TodoGroupDbContext CreateDbContext() + [Fact] + public async Task DeleteTodoDeletesTodoInDatabase() { - var options = new DbContextOptionsBuilder() - .UseInMemoryDatabase("TodoUnitTestsDb") - .Options; + //Arrange + var existingTodo = new Todo() + { + Id = 1, + Title = "Test title 1", + IsDone = false + }; + + var todos = new List() { existingTodo }; + + var mock = new Mock(); + + mock.Setup(m => m.Find(It.Is(id => id == existingTodo.Id))).ReturnsAsync(existingTodo); + + mock.Setup(m => m.Find(It.Is(id => id == 2))).ReturnsAsync((Todo?)null); + + mock.Setup(m => m.Remove(It.Is(t => t.Id == 1))) + .Callback(t => todos.Remove(t)) + .Returns(Task.CompletedTask); + + //Act + var noContentResult = (NoContent)await TodoEndpoints.DeleteTodo(existingTodo.Id, mock.Object); + var notFoundResult = (NotFound)await TodoEndpoints.DeleteTodo(2, mock.Object); + + //Assert + Assert.Equal(204, noContentResult.StatusCode); + Assert.Empty(todos); - return new TodoGroupDbContext(options); + Assert.Equal(404, notFoundResult.StatusCode); } } diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs index 099891ce23fd..57c50e29ba40 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs @@ -2,11 +2,13 @@ using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using todo_group; +using todo_group.Services; var builder = WebApplication.CreateBuilder(args); var connection = new SqliteConnection("DataSource=:memory:"); connection.Open(); +builder.Services.AddTransient(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddDbContext(options => @@ -68,6 +70,7 @@ app.Logger.LogInformation($"{context.HttpContext.Request.Path.Value} took {(end - start).TotalMilliseconds}ms"); return result; }); + todos.MapDelete("/{id}", TodoEndpoints.DeleteTodo); app.Run(); diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/ITodoService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/ITodoService.cs new file mode 100644 index 000000000000..c72497814881 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/ITodoService.cs @@ -0,0 +1,13 @@ +using Data; + +namespace todo_group.Services +{ + public interface ITodoService + { + Task> GetAll(); + ValueTask Find(int id); + Task Add(Todo todo); + Task Update(Todo todo); + Task Remove(Todo todo); + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/TodoService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/TodoService.cs new file mode 100644 index 000000000000..6cc873d1fc13 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/TodoService.cs @@ -0,0 +1,43 @@ +using Data; +using Microsoft.EntityFrameworkCore; + +namespace todo_group.Services +{ + public class TodoService : ITodoService + { + private readonly TodoGroupDbContext _dbContext; + + public TodoService(TodoGroupDbContext dbContext) + { + _dbContext = dbContext; + } + + public async ValueTask Find(int id) + { + return await _dbContext.Todos.FindAsync(id); + } + + public async Task> GetAll() + { + return await _dbContext.Todos.ToListAsync(); + } + + public async Task Add(Todo todo) + { + await _dbContext.Todos.AddAsync(todo); + await _dbContext.SaveChangesAsync(); + } + + public async Task Update(Todo todo) + { + _dbContext.Todos.Update(todo); + await _dbContext.SaveChangesAsync(); + } + + public async Task Remove(Todo todo) + { + _dbContext.Todos.Remove(todo); + await _dbContext.SaveChangesAsync(); + } + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpoints.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpoints.cs index 0dadb763fe6a..5ef7f8d65b4d 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpoints.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpoints.cs @@ -1,30 +1,32 @@ using Data; -using Microsoft.EntityFrameworkCore; +using todo_group.Services; namespace todo_group; public class TodoEndpoints { // get all todos - public static async Task GetAllTodos(TodoGroupDbContext database) + public static async Task GetAllTodos(ITodoService todoService) { - var todos = await database.Todos.ToListAsync(); + var todos = await todoService.GetAll(); return TypedResults.Ok(todos); } // get todo by id - public static async Task GetTodo(int id, TodoGroupDbContext database) + public static async Task GetTodo(int id, ITodoService todoService) { - var todo = await database.Todos.FindAsync(id); + var todo = await todoService.Find(id); + if (todo != null) { return TypedResults.Ok(todo); } + return TypedResults.NotFound(); } // create todo - public static async Task CreateTodo(TodoDto todo, TodoGroupDbContext database) + public static async Task CreateTodo(TodoDto todo, ITodoService todoService) { var newTodo = new Todo { @@ -32,19 +34,25 @@ public static async Task CreateTodo(TodoDto todo, TodoGroupDbContext da Description = todo.Description, IsDone = todo.IsDone }; - await database.Todos.AddAsync(newTodo); - await database.SaveChangesAsync(); + + await todoService.Add(newTodo); + return TypedResults.Created($"/public/todos/{newTodo.Id}", newTodo); } // update todo - public static async Task UpdateTodo(Todo todo, TodoGroupDbContext database) + public static async Task UpdateTodo(Todo todo, ITodoService todoService) { - var existingTodo = await database.Todos.FindAsync(todo.Id); + var existingTodo = await todoService.Find(todo.Id); + if (existingTodo != null) { - database.Update(todo); - await database.SaveChangesAsync(); + existingTodo.Title = todo.Title; + existingTodo.Description = todo.Description; + existingTodo.IsDone = todo.IsDone; + + await todoService.Update(existingTodo); + return TypedResults.Created($"/public/todos/{existingTodo.Id}", existingTodo); } @@ -52,15 +60,16 @@ public static async Task UpdateTodo(Todo todo, TodoGroupDbContext datab } // delete todo - public static async Task DeleteTodo(int id, TodoGroupDbContext database) + public static async Task DeleteTodo(int id, ITodoService todoService) { - var todo = await database.Todos.FindAsync(id); + var todo = await todoService.Find(id); + if (todo != null) { - database.Todos.Remove(todo); - await database.SaveChangesAsync(); + await todoService.Remove(todo); return TypedResults.NoContent(); } + return TypedResults.NotFound(); } } From 698f853e79d903dd654c4a0807fbd63653e04d36 Mon Sep 17 00:00:00 2001 From: Fiyaz Bin Hasan Date: Sun, 21 Aug 2022 13:32:22 +0600 Subject: [PATCH 05/18] work --- .../7.0-samples/MinApiTests/TodoTests.cs | 54 ++++++++++++------- .../7.0-samples/todo-group/Data/Todo.cs | 7 +-- .../7.0-samples/todo-group/Data/TodoDto.cs | 6 +-- .../todo-group/Data/TodoGroupDbContext.cs | 7 +-- .../7.0-samples/todo-group/Program.cs | 27 +++++----- .../todo-group/Services/ITodoService.cs | 6 ++- .../todo-group/Services/TodoService.cs | 2 +- .../7.0-samples/todo-group/TodoEndpoints.cs | 20 +++---- .../7.0-samples/todo-group/Utilities.cs | 6 +-- 9 files changed, 78 insertions(+), 57 deletions(-) diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoTests.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoTests.cs index f6c9773984be..666bc0c4d6dd 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoTests.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoTests.cs @@ -1,9 +1,8 @@ -using todo_group; -using Data; using Microsoft.AspNetCore.Http.HttpResults; using Moq; +using todo_group; +using todo_group.Data; using todo_group.Services; -using System.Runtime.CompilerServices; namespace MinApiTests; @@ -15,14 +14,16 @@ public async Task GetTodoReturnsTodoFromDatabase() // Arrange var mock = new Mock(); - mock.Setup(m => m.Find(It.Is(id => id == 1))).ReturnsAsync(new Todo - { - Id = 1, - Title = "Test title", - IsDone = false - }); + mock.Setup(m => m.Find(It.Is(id => id == 1))) + .ReturnsAsync(new Todo + { + Id = 1, + Title = "Test title", + IsDone = false + }); - mock.Setup(m => m.Find(It.Is(id => id == 2))).ReturnsAsync((Todo?)null); + mock.Setup(m => m.Find(It.Is(id => id == 2))) + .ReturnsAsync((Todo?)null); // Act var okResult = (Ok)await TodoEndpoints.GetTodo(1, mock.Object); @@ -40,6 +41,8 @@ public async Task GetTodoReturnsTodoFromDatabase() public async Task CreateTodoCreatesTodoInDatabase() { //Arrange + var todos = new List(); + var newTodo = new TodoDto { Title = "Test title", @@ -50,6 +53,7 @@ public async Task CreateTodoCreatesTodoInDatabase() var mock = new Mock(); mock.Setup(m => m.Add(It.Is(t => t.Title == newTodo.Title && t.Description == newTodo.Description && t.IsDone == newTodo.IsDone))) + .Callback(todo => todos.Add(todo)) .Returns(Task.CompletedTask); //Act @@ -59,20 +63,28 @@ public async Task CreateTodoCreatesTodoInDatabase() Assert.Equal(201, createdResult.StatusCode); Assert.NotNull(createdResult.Location); Assert.IsAssignableFrom(createdResult.Value); + + Assert.NotEmpty(todos); + Assert.Collection(todos, todo => + { + Assert.Equal("Test title", todo.Title); + Assert.Equal("Test description", todo.Description); + Assert.False(todo.IsDone); + }); } [Fact] public async Task UpdateTodoUpdatesTodoInDatabase() { //Arrange - var existingTodo = new Todo() + var existingTodo = new Todo { Id = 1, Title = "Exiting test title", IsDone = false }; - var updatedTodo = new Todo() + var updatedTodo = new Todo { Id = 1, Title = "Updated test title", @@ -81,9 +93,11 @@ public async Task UpdateTodoUpdatesTodoInDatabase() var mock = new Mock(); - mock.Setup(m => m.Find(It.Is(id => id == 1))).ReturnsAsync(existingTodo); + mock.Setup(m => m.Find(It.Is(id => id == 1))) + .ReturnsAsync(existingTodo); - mock.Setup(m => m.Find(It.Is(id => id == 2))).ReturnsAsync((Todo?)null); + mock.Setup(m => m.Find(It.Is(id => id == 2))) + .ReturnsAsync((Todo?)null); mock.Setup(m => m.Update(It.Is(t => t.Id == updatedTodo.Id && t.Description == updatedTodo.Description && t.IsDone == updatedTodo.IsDone))) .Callback((todo) => existingTodo = todo) @@ -91,7 +105,7 @@ public async Task UpdateTodoUpdatesTodoInDatabase() //Act var createdResult = (Created)await TodoEndpoints.UpdateTodo(updatedTodo, mock.Object); - var notFoundResult = (NotFound)await TodoEndpoints.UpdateTodo(new Todo() { Id = 2, Title = "Invalid Title" }, mock.Object); + var notFoundResult = (NotFound)await TodoEndpoints.UpdateTodo(new Todo { Id = 2, Title = "Invalid Title" }, mock.Object); //Assert Assert.Equal(201, createdResult.StatusCode); @@ -108,20 +122,22 @@ public async Task UpdateTodoUpdatesTodoInDatabase() public async Task DeleteTodoDeletesTodoInDatabase() { //Arrange - var existingTodo = new Todo() + var existingTodo = new Todo { Id = 1, Title = "Test title 1", IsDone = false }; - var todos = new List() { existingTodo }; + var todos = new List { existingTodo }; var mock = new Mock(); - mock.Setup(m => m.Find(It.Is(id => id == existingTodo.Id))).ReturnsAsync(existingTodo); + mock.Setup(m => m.Find(It.Is(id => id == existingTodo.Id))) + .ReturnsAsync(existingTodo); - mock.Setup(m => m.Find(It.Is(id => id == 2))).ReturnsAsync((Todo?)null); + mock.Setup(m => m.Find(It.Is(id => id == 2))) + .ReturnsAsync((Todo?)null); mock.Setup(m => m.Remove(It.Is(t => t.Id == 1))) .Callback(t => todos.Remove(t)) diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/Todo.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/Todo.cs index 1d231d41b812..295125b1b7af 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/Todo.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/Todo.cs @@ -1,8 +1,9 @@ -namespace Data; +namespace todo_group.Data; + public class Todo { public int Id { get; set; } - public string Title { get; set; } = String.Empty; - public string Description { get; set; } = String.Empty; + public string Title { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; public bool IsDone { get; set; } } diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoDto.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoDto.cs index a75996caaa43..e1df4b5ec78a 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoDto.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoDto.cs @@ -1,8 +1,8 @@ -namespace Data; +namespace todo_group.Data; public class TodoDto { - public string Title { get; set; } = String.Empty; - public string Description { get; set; } = String.Empty; + public string Title { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; public bool IsDone { get; set; } } diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoGroupDbContext.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoGroupDbContext.cs index d17e99d46b59..426231b38b58 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoGroupDbContext.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoGroupDbContext.cs @@ -1,14 +1,15 @@ using Microsoft.EntityFrameworkCore; -namespace Data; +namespace todo_group.Data; -public class TodoGroupDbContext : DbContext +public sealed class TodoGroupDbContext : DbContext { public TodoGroupDbContext(DbContextOptions options) : base(options) { + Database.OpenConnection(); Database.EnsureCreated(); } - public DbSet Todos { get; set; } = default!; + public DbSet Todos => Set(); } diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs index 57c50e29ba40..aa2130b48db7 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs @@ -1,19 +1,16 @@ -using Data; -using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using todo_group; +using todo_group.Data; using todo_group.Services; var builder = WebApplication.CreateBuilder(args); -var connection = new SqliteConnection("DataSource=:memory:"); -connection.Open(); builder.Services.AddTransient(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddDbContext(options => { - options.UseSqlite(connection); + options.UseSqlite("DataSource=:memory:"); }); var app = builder.Build(); @@ -28,11 +25,13 @@ app.MapGet("/", () => "Hello World!"); // todo endpoints -var todos = app.MapGroup("/todos").WithTags("Todo Endpoints").AddEndpointFilter(async (context, next) => -{ - app.Logger.LogInformation("Accessing todo endpoints"); - return await next(context); -}); +var todos = app.MapGroup("/todos") + .WithTags("Todo Endpoints") + .AddEndpointFilter(async (context, next) => + { + app.Logger.LogInformation("Accessing todo endpoints"); + return await next(context); + }); todos.MapGet("/", TodoEndpoints.GetAllTodos); todos.MapGet("/{id}", TodoEndpoints.GetTodo); @@ -50,17 +49,17 @@ .AddEndpointFilter(async (efiContext, next) => { var tdparam = efiContext.GetArgument(0); - + var validationErrors = Utilities.IsValid(tdparam); - + if (validationErrors.Any()) { return Results.ValidationProblem(validationErrors); } - + return await next(efiContext); }); - + todos.MapPut("/{id}", TodoEndpoints.UpdateTodo).AddEndpointFilter(async (context, next) => { // log time taken to process diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/ITodoService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/ITodoService.cs index c72497814881..53ea16a2c640 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/ITodoService.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/ITodoService.cs @@ -1,13 +1,17 @@ -using Data; +using todo_group.Data; namespace todo_group.Services { public interface ITodoService { Task> GetAll(); + ValueTask Find(int id); + Task Add(Todo todo); + Task Update(Todo todo); + Task Remove(Todo todo); } } diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/TodoService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/TodoService.cs index 6cc873d1fc13..aa5921192215 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/TodoService.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/TodoService.cs @@ -1,5 +1,5 @@ -using Data; using Microsoft.EntityFrameworkCore; +using todo_group.Data; namespace todo_group.Services { diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpoints.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpoints.cs index 5ef7f8d65b4d..7f408a2b3ea0 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpoints.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpoints.cs @@ -1,4 +1,4 @@ -using Data; +using todo_group.Data; using todo_group.Services; namespace todo_group; @@ -16,12 +16,12 @@ public static async Task GetAllTodos(ITodoService todoService) public static async Task GetTodo(int id, ITodoService todoService) { var todo = await todoService.Find(id); - + if (todo != null) { return TypedResults.Ok(todo); } - + return TypedResults.NotFound(); } @@ -34,9 +34,9 @@ public static async Task CreateTodo(TodoDto todo, ITodoService todoServ Description = todo.Description, IsDone = todo.IsDone }; - + await todoService.Add(newTodo); - + return TypedResults.Created($"/public/todos/{newTodo.Id}", newTodo); } @@ -44,15 +44,15 @@ public static async Task CreateTodo(TodoDto todo, ITodoService todoServ public static async Task UpdateTodo(Todo todo, ITodoService todoService) { var existingTodo = await todoService.Find(todo.Id); - + if (existingTodo != null) { existingTodo.Title = todo.Title; existingTodo.Description = todo.Description; existingTodo.IsDone = todo.IsDone; - + await todoService.Update(existingTodo); - + return TypedResults.Created($"/public/todos/{existingTodo.Id}", existingTodo); } @@ -63,13 +63,13 @@ public static async Task UpdateTodo(Todo todo, ITodoService todoService public static async Task DeleteTodo(int id, ITodoService todoService) { var todo = await todoService.Find(id); - + if (todo != null) { await todoService.Remove(todo); return TypedResults.NoContent(); } - + return TypedResults.NotFound(); } } diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Utilities.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Utilities.cs index d981e9b3ce93..935655a7fac7 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Utilities.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Utilities.cs @@ -1,4 +1,4 @@ -using Data; +using todo_group.Data; namespace todo_group { @@ -7,8 +7,8 @@ public static class Utilities public static Dictionary IsValid(TodoDto td) { Dictionary errors = new(); - - if(string.IsNullOrEmpty(td.Title)) + + if (string.IsNullOrEmpty(td.Title)) { errors.Add("todo.name.errors", new[] { "Name is empty" }); } From 702021305e982e1f466423c9d9cbf7bfa33843cc Mon Sep 17 00:00:00 2001 From: Fiyaz Bin Hasan Date: Sun, 21 Aug 2022 20:38:46 +0600 Subject: [PATCH 06/18] work --- .../MinApiTests/TodoUnitTestsV1.cs | 152 ++++++++++++++++++ .../{TodoTests.cs => TodoUnitTestsV2.cs} | 16 +- .../todo-group/Data/TodoGroupDbContext.cs | 11 +- .../20220821140431_InitialCreate.Designer.cs | 46 ++++++ .../20220821140431_InitialCreate.cs | 36 +++++ .../TodoGroupDbContextModelSnapshot.cs | 43 +++++ .../7.0-samples/todo-group/Program.cs | 104 +++++++++--- .../7.0-samples/todo-group/TodoEndpointsV1.cs | 79 +++++++++ .../{TodoEndpoints.cs => TodoEndpointsV2.cs} | 2 +- .../7.0-samples/todo-group/todo-group.csproj | 14 +- 10 files changed, 461 insertions(+), 42 deletions(-) create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoUnitTestsV1.cs rename aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/{TodoTests.cs => TodoUnitTestsV2.cs} (84%) create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/20220821140431_InitialCreate.Designer.cs create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/20220821140431_InitialCreate.cs create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/TodoGroupDbContextModelSnapshot.cs create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV1.cs rename aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/{TodoEndpoints.cs => TodoEndpointsV2.cs} (98%) diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoUnitTestsV1.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoUnitTestsV1.cs new file mode 100644 index 000000000000..9c6a4bd7b010 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoUnitTestsV1.cs @@ -0,0 +1,152 @@ +using Microsoft.AspNetCore.Http.HttpResults; +using Microsoft.EntityFrameworkCore; +using todo_group.Data; +using todo_group; + +namespace MinApiTests +{ + public class TodoUnitTestsV1 + { + [Fact] + public async Task GetTodoReturnsTodoFromDatabase() + { + // Arrange + var options = new DbContextOptionsBuilder() + .UseInMemoryDatabase($"InMemoryTestDb-{DateTime.Now}") + .Options; + + await using var context = new TodoGroupDbContext(options); + + context.Todos.Add(new Todo + { + Id = 1, + Title = "Test title", + Description = "Test description", + IsDone = false + }); + + await context.SaveChangesAsync(); + + // Act + var okResult = (Ok)await TodoEndpointsV1.GetTodo(1, context); + var notFoundResult = (NotFound)await TodoEndpointsV1.GetTodo(404, context); + + //Assert + Assert.Equal(200, okResult.StatusCode); + var foundTodo = Assert.IsAssignableFrom(okResult.Value); + Assert.Equal(1, foundTodo.Id); + + Assert.Equal(404, notFoundResult.StatusCode); + } + + [Fact] + public async Task CreateTodoCreatesTodoInDatabase() + { + //Arrange + var options = new DbContextOptionsBuilder() + .UseInMemoryDatabase($"InMemoryTestDb-{DateTime.Now}") + .Options; + + await using var context = new TodoGroupDbContext(options); + + var newTodo = new TodoDto + { + Title = "Test title", + Description = "Test description", + IsDone = false + }; + + //Act + var createdResult = (Created)await TodoEndpointsV1.CreateTodo(newTodo, context); + + //Assert + Assert.Equal(201, createdResult.StatusCode); + Assert.NotNull(createdResult.Location); + Assert.IsAssignableFrom(createdResult.Value); + + Assert.NotEmpty(context.Todos); + Assert.Collection(context.Todos, todo => + { + Assert.Equal("Test title", todo.Title); + Assert.Equal("Test description", todo.Description); + Assert.False(todo.IsDone); + }); + } + + [Fact] + public async Task UpdateTodoUpdatesTodoInDatabase() + { + //Arrange + var options = new DbContextOptionsBuilder() + .UseInMemoryDatabase($"InMemoryTestDb-{DateTime.Now}") + .Options; + + await using var context = new TodoGroupDbContext(options); + + context.Todos.Add(new Todo + { + Id = 1, + Title = "Exiting test title", + IsDone = false + }); + + await context.SaveChangesAsync(); + + var updatedTodo = new Todo + { + Id = 1, + Title = "Updated test title", + IsDone = true + }; + + //Act + var createdResult = (Created)await TodoEndpointsV1.UpdateTodo(updatedTodo, context); + var notFoundResult = (NotFound)await TodoEndpointsV1.UpdateTodo(new Todo { Id = 2, Title = "Invalid Title" }, context); + + //Assert + Assert.Equal(201, createdResult.StatusCode); + Assert.NotNull(createdResult.Location); + Assert.IsAssignableFrom(createdResult.Value); + + Assert.Equal(404, notFoundResult.StatusCode); + + var todoInDb = await context.Todos.FindAsync(1); + + Assert.NotNull(todoInDb); + Assert.Equal("Updated test title", todoInDb!.Title); + Assert.True(todoInDb.IsDone); + } + + [Fact] + public async Task DeleteTodoDeletesTodoInDatabase() + { + //Arrange + var existingTodo = new Todo() + { + Id = 1, + Title = "Exiting test title", + IsDone = false + }; + + var options = new DbContextOptionsBuilder() + .UseInMemoryDatabase($"InMemoryTestDb-{DateTime.Now}") + .Options; + + await using var context = new TodoGroupDbContext(options); + + context.Todos.Add(existingTodo); + + await context.SaveChangesAsync(); + + //Act + var noContentResult = (NoContent)await TodoEndpointsV1.DeleteTodo(existingTodo.Id, context); + var notFoundResult = (NotFound)await TodoEndpointsV1.DeleteTodo(2, context); + + //Assert + Assert.Equal(204, noContentResult.StatusCode); + Assert.Empty(context.Todos); + + Assert.Equal(404, notFoundResult.StatusCode); + } + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoTests.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoUnitTestsV2.cs similarity index 84% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoTests.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoUnitTestsV2.cs index 666bc0c4d6dd..892521f4e93f 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoTests.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoUnitTestsV2.cs @@ -6,7 +6,7 @@ namespace MinApiTests; -public class TodoTests +public class TodoUnitTestsV2 { [Fact] public async Task GetTodoReturnsTodoFromDatabase() @@ -26,8 +26,8 @@ public async Task GetTodoReturnsTodoFromDatabase() .ReturnsAsync((Todo?)null); // Act - var okResult = (Ok)await TodoEndpoints.GetTodo(1, mock.Object); - var notFoundResult = (NotFound)await TodoEndpoints.GetTodo(404, mock.Object); + var okResult = (Ok)await TodoEndpointsV2.GetTodo(1, mock.Object); + var notFoundResult = (NotFound)await TodoEndpointsV2.GetTodo(404, mock.Object); //Assert Assert.Equal(200, okResult.StatusCode); @@ -57,7 +57,7 @@ public async Task CreateTodoCreatesTodoInDatabase() .Returns(Task.CompletedTask); //Act - var createdResult = (Created)await TodoEndpoints.CreateTodo(newTodo, mock.Object); + var createdResult = (Created)await TodoEndpointsV2.CreateTodo(newTodo, mock.Object); //Assert Assert.Equal(201, createdResult.StatusCode); @@ -104,8 +104,8 @@ public async Task UpdateTodoUpdatesTodoInDatabase() .Returns(Task.CompletedTask); //Act - var createdResult = (Created)await TodoEndpoints.UpdateTodo(updatedTodo, mock.Object); - var notFoundResult = (NotFound)await TodoEndpoints.UpdateTodo(new Todo { Id = 2, Title = "Invalid Title" }, mock.Object); + var createdResult = (Created)await TodoEndpointsV2.UpdateTodo(updatedTodo, mock.Object); + var notFoundResult = (NotFound)await TodoEndpointsV2.UpdateTodo(new Todo { Id = 2, Title = "Invalid Title" }, mock.Object); //Assert Assert.Equal(201, createdResult.StatusCode); @@ -144,8 +144,8 @@ public async Task DeleteTodoDeletesTodoInDatabase() .Returns(Task.CompletedTask); //Act - var noContentResult = (NoContent)await TodoEndpoints.DeleteTodo(existingTodo.Id, mock.Object); - var notFoundResult = (NotFound)await TodoEndpoints.DeleteTodo(2, mock.Object); + var noContentResult = (NoContent)await TodoEndpointsV2.DeleteTodo(existingTodo.Id, mock.Object); + var notFoundResult = (NotFound)await TodoEndpointsV2.DeleteTodo(2, mock.Object); //Assert Assert.Equal(204, noContentResult.StatusCode); diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoGroupDbContext.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoGroupDbContext.cs index 426231b38b58..098be78e506c 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoGroupDbContext.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoGroupDbContext.cs @@ -2,14 +2,13 @@ namespace todo_group.Data; -public sealed class TodoGroupDbContext : DbContext +public class TodoGroupDbContext : DbContext { - public TodoGroupDbContext(DbContextOptions options) + public DbSet Todos => Set(); + + public TodoGroupDbContext(DbContextOptions options) : base(options) { - Database.OpenConnection(); - Database.EnsureCreated(); + } - - public DbSet Todos => Set(); } diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/20220821140431_InitialCreate.Designer.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/20220821140431_InitialCreate.Designer.cs new file mode 100644 index 000000000000..07ce22dc78fd --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/20220821140431_InitialCreate.Designer.cs @@ -0,0 +1,46 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using todo_group.Data; + +#nullable disable + +namespace todo_group.Migrations +{ + [DbContext(typeof(TodoGroupDbContext))] + [Migration("20220821140431_InitialCreate")] + partial class InitialCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "7.0.0-preview.7.22376.2"); + + modelBuilder.Entity("todo_group.Data.Todo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Description") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsDone") + .HasColumnType("INTEGER"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Todos"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/20220821140431_InitialCreate.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/20220821140431_InitialCreate.cs new file mode 100644 index 000000000000..741122637a54 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/20220821140431_InitialCreate.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace todo_group.Migrations +{ + /// + public partial class InitialCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Todos", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Title = table.Column(type: "TEXT", nullable: false), + Description = table.Column(type: "TEXT", nullable: false), + IsDone = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Todos", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Todos"); + } + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/TodoGroupDbContextModelSnapshot.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/TodoGroupDbContextModelSnapshot.cs new file mode 100644 index 000000000000..0649cb5b07ce --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/TodoGroupDbContextModelSnapshot.cs @@ -0,0 +1,43 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using todo_group.Data; + +#nullable disable + +namespace todo_group.Migrations +{ + [DbContext(typeof(TodoGroupDbContext))] + partial class TodoGroupDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "7.0.0-preview.7.22376.2"); + + modelBuilder.Entity("todo_group.Data.Todo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Description") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsDone") + .HasColumnType("INTEGER"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Todos"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs index aa2130b48db7..f81175ab6f45 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs @@ -10,11 +10,17 @@ builder.Services.AddSwaggerGen(); builder.Services.AddDbContext(options => { - options.UseSqlite("DataSource=:memory:"); + var path = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + options.UseSqlite($"Data Source={Path.Join(path, "todo_group.db")}"); }); var app = builder.Build(); +using var scope = app.Services.CreateScope(); +var db = scope.ServiceProvider.GetService(); +db?.Database.MigrateAsync(); + + if (app.Environment.IsDevelopment()) { // localhost:{port}/swagger @@ -24,19 +30,19 @@ app.MapGet("/", () => "Hello World!"); -// todo endpoints -var todos = app.MapGroup("/todos") - .WithTags("Todo Endpoints") - .AddEndpointFilter(async (context, next) => - { - app.Logger.LogInformation("Accessing todo endpoints"); - return await next(context); - }); +// todoV1 endpoints +var todosV1 = app.MapGroup("/todos/v1") + .WithTags("Todo Endpoints") + .AddEndpointFilter(async (context, next) => + { + app.Logger.LogInformation("Accessing todo endpoints"); + return await next(context); + }); -todos.MapGet("/", TodoEndpoints.GetAllTodos); -todos.MapGet("/{id}", TodoEndpoints.GetTodo); +todosV1.MapGet("/", TodoEndpointsV1.GetAllTodos); +todosV1.MapGet("/{id}", TodoEndpointsV1.GetTodo); -todos.MapPost("/", TodoEndpoints.CreateTodo) +todosV1.MapPost("/", TodoEndpointsV1.CreateTodo) .AddEndpointFilter(async (context, next) => { // log time taken to process @@ -48,9 +54,9 @@ }) .AddEndpointFilter(async (efiContext, next) => { - var tdparam = efiContext.GetArgument(0); + var param = efiContext.GetArgument(0); - var validationErrors = Utilities.IsValid(tdparam); + var validationErrors = Utilities.IsValid(param); if (validationErrors.Any()) { @@ -60,16 +66,66 @@ return await next(efiContext); }); -todos.MapPut("/{id}", TodoEndpoints.UpdateTodo).AddEndpointFilter(async (context, next) => -{ - // log time taken to process - var start = DateTime.Now; - var result = await next(context); - var end = DateTime.Now; - app.Logger.LogInformation($"{context.HttpContext.Request.Path.Value} took {(end - start).TotalMilliseconds}ms"); - return result; -}); +todosV1.MapPut("/{id}", TodoEndpointsV1.UpdateTodo) + .AddEndpointFilter(async (context, next) => + { + // log time taken to process + var start = DateTime.Now; + var result = await next(context); + var end = DateTime.Now; + app.Logger.LogInformation($"{context.HttpContext.Request.Path.Value} took {(end - start).TotalMilliseconds}ms"); + return result; + }); + +todosV1.MapDelete("/{id}", TodoEndpointsV1.DeleteTodo); + +// todoV2 endpoints +var todosV2 = app.MapGroup("/todos/v2") + .WithTags("Todo Endpoints") + .AddEndpointFilter(async (context, next) => + { + app.Logger.LogInformation("Accessing todo endpoints"); + return await next(context); + }); + +todosV2.MapGet("/", TodoEndpointsV2.GetAllTodos); +todosV2.MapGet("/{id}", TodoEndpointsV2.GetTodo); + +todosV2.MapPost("/", TodoEndpointsV2.CreateTodo) + .AddEndpointFilter(async (context, next) => + { + // log time taken to process + var start = DateTime.Now; + var result = await next(context); + var end = DateTime.Now; + app.Logger.LogInformation($"{context.HttpContext.Request.Path.Value} took {(end - start).TotalMilliseconds}ms"); + return result; + }) + .AddEndpointFilter(async (efiContext, next) => + { + var param = efiContext.GetArgument(0); + + var validationErrors = Utilities.IsValid(param); + + if (validationErrors.Any()) + { + return Results.ValidationProblem(validationErrors); + } + + return await next(efiContext); + }); + +todosV2.MapPut("/{id}", TodoEndpointsV2.UpdateTodo) + .AddEndpointFilter(async (context, next) => + { + // log time taken to process + var start = DateTime.Now; + var result = await next(context); + var end = DateTime.Now; + app.Logger.LogInformation($"{context.HttpContext.Request.Path.Value} took {(end - start).TotalMilliseconds}ms"); + return result; + }); -todos.MapDelete("/{id}", TodoEndpoints.DeleteTodo); +todosV2.MapDelete("/{id}", TodoEndpointsV2.DeleteTodo); app.Run(); diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV1.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV1.cs new file mode 100644 index 000000000000..05b4dec1feb7 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV1.cs @@ -0,0 +1,79 @@ +using Microsoft.EntityFrameworkCore; +using todo_group.Data; + +namespace todo_group +{ + public class TodoEndpointsV1 + { + // get all todos + public static async Task GetAllTodos(TodoGroupDbContext database) + { + var todos = await database.Todos.ToListAsync(); + return TypedResults.Ok(todos); + } + + // get todo by id + public static async Task GetTodo(int id, TodoGroupDbContext database) + { + var todo = await database.Todos.FindAsync(id); + + if (todo != null) + { + return TypedResults.Ok(todo); + } + + return TypedResults.NotFound(); + } + + // create todo + public static async Task CreateTodo(TodoDto todo, TodoGroupDbContext database) + { + var newTodo = new Todo + { + Title = todo.Title, + Description = todo.Description, + IsDone = todo.IsDone + }; + + await database.Todos.AddAsync(newTodo); + await database.SaveChangesAsync(); + + return TypedResults.Created($"/public/todos/{newTodo.Id}", newTodo); + } + + // update todo + public static async Task UpdateTodo(Todo todo, TodoGroupDbContext database) + { + var existingTodo = await database.Todos.FindAsync(todo.Id); + + if (existingTodo != null) + { + existingTodo.Title = todo.Title; + existingTodo.Description = todo.Description; + existingTodo.IsDone = todo.IsDone; + + await database.SaveChangesAsync(); + + return TypedResults.Created($"/public/todos/{existingTodo.Id}", existingTodo); + } + + return TypedResults.NotFound(); + } + + // delete todo + public static async Task DeleteTodo(int id, TodoGroupDbContext database) + { + var todo = await database.Todos.FindAsync(id); + + if (todo != null) + { + database.Todos.Remove(todo); + await database.SaveChangesAsync(); + + return TypedResults.NoContent(); + } + + return TypedResults.NotFound(); + } + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpoints.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV2.cs similarity index 98% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpoints.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV2.cs index 7f408a2b3ea0..3676cdb42ba0 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpoints.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV2.cs @@ -3,7 +3,7 @@ namespace todo_group; -public class TodoEndpoints +public class TodoEndpointsV2 { // get all todos public static async Task GetAllTodos(ITodoService todoService) diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/todo-group.csproj b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/todo-group.csproj index 80f875509fdf..7a488c9bdb97 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/todo-group.csproj +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/todo-group.csproj @@ -8,9 +8,17 @@ - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + From 985f7c596f734166b956616582d8d2405de4e00b Mon Sep 17 00:00:00 2001 From: Fiyaz Bin Hasan Date: Tue, 23 Aug 2022 02:19:17 +0600 Subject: [PATCH 07/18] unit and integration tests --- .../AuthorizedEndpointsIntegrationTests.cs | 48 ++++++++++ .../Helpers/TestAuthHandler.cs | 38 ++++++++ .../Helpers/TestEmailService.cs | 13 +++ .../Helpers/TestWebApplicationFactory.cs | 33 +++++++ .../TodoIntegrationTestsV1.cs | 77 ++++++++++++++++ .../TodoIntegrationTestsV2.cs | 87 +++++++++++++++++++ .../MinApiTests/MinApiTests.csproj | 1 + .../MinApiTests/UnitTests/Helpers/MockDb.cs | 14 +++ .../TodoInMemoryTests.cs} | 37 +++----- .../TodoMoqTests.cs} | 4 +- .../todo-group/Data/TodoGroupDbContext.cs | 3 +- .../20220821140431_InitialCreate.cs | 2 +- .../7.0-samples/todo-group/Program.cs | 13 +++ .../todo-group/Services/EmailService.cs | 11 +++ .../todo-group/Services/IEmailService.cs | 7 ++ .../todo-group/Services/ITodoService.cs | 2 + .../todo-group/Services/TodoService.cs | 13 ++- .../7.0-samples/todo-group/TodoEndpointsV2.cs | 6 ++ .../7.0-samples/todo-group/Utilities.cs | 4 +- .../7.0-samples/todo-group/todo-group.csproj | 2 +- 20 files changed, 378 insertions(+), 37 deletions(-) create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/AuthorizedEndpointsIntegrationTests.cs create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestAuthHandler.cs create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestEmailService.cs create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestWebApplicationFactory.cs create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/Helpers/MockDb.cs rename aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/{TodoUnitTestsV1.cs => UnitTests/TodoInMemoryTests.cs} (77%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/{TodoUnitTestsV2.cs => UnitTests/TodoMoqTests.cs} (98%) create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/EmailService.cs create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/IEmailService.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/AuthorizedEndpointsIntegrationTests.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/AuthorizedEndpointsIntegrationTests.cs new file mode 100644 index 000000000000..7b3e66b5b411 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/AuthorizedEndpointsIntegrationTests.cs @@ -0,0 +1,48 @@ +using System.Net; +using System.Net.Http.Headers; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using MinApiTests.IntegrationTests.Helpers; + +namespace MinApiTests.IntegrationTests +{ + public class AuthorizedEndpointsIntegrationTests : IClassFixture> + { + private readonly TestWebApplicationFactory _factory; + + public AuthorizedEndpointsIntegrationTests(TestWebApplicationFactory factory) + { + _factory = factory; + } + + public static IEnumerable AdminFlags => new List + { + new object[] { "false", HttpStatusCode.Forbidden }, + new object[] { "true", HttpStatusCode.OK } + }; + + [Theory] + [MemberData(nameof(AdminFlags))] + public async Task GetAdminEndpointIsReturnedForAnAuthorizedRequest(string isAdmin, HttpStatusCode code) + { + // Arrange + var client = _factory.WithWebHostBuilder(builder => + { + builder.ConfigureTestServices(services => + { + services.AddAuthentication("Test") + .AddScheme("Test", + options => options.IsAdmin = isAdmin); + }); + }).CreateClient(); + + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Test"); + + //Act + var response = await client.GetAsync("/admin"); + + // Assert + Assert.Equal(code, response.StatusCode); + } + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestAuthHandler.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestAuthHandler.cs new file mode 100644 index 000000000000..c994d59a445b --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestAuthHandler.cs @@ -0,0 +1,38 @@ +using System.Security.Claims; +using System.Text.Encodings.Web; +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace MinApiTests.IntegrationTests.Helpers +{ + public class TestAuthenticationSchemeOptions : AuthenticationSchemeOptions + { + public string IsAdmin { get; set; } = "false"; + } + + public class TestAuthHandler : AuthenticationHandler + { + public TestAuthHandler(IOptionsMonitor options, + ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + : base(options, logger, encoder, clock) + { + } + + protected override Task HandleAuthenticateAsync() + { + var claims = new[] + { + new Claim("admin", Options.IsAdmin) + }; + + var identity = new ClaimsIdentity(claims, "Test"); + var principal = new ClaimsPrincipal(identity); + var ticket = new AuthenticationTicket(principal, "Test"); + + var result = AuthenticateResult.Success(ticket); + + return Task.FromResult(result); + } + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestEmailService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestEmailService.cs new file mode 100644 index 000000000000..85608d6028f8 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestEmailService.cs @@ -0,0 +1,13 @@ +using todo_group.Services; + +namespace MinApiTests.IntegrationTests.Helpers +{ + public class TestEmailService : IEmailService + { + public Task Send(string emaidAddress, string body) + { + // You don't want to send real email when running integration tests + return Task.CompletedTask; + } + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestWebApplicationFactory.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestWebApplicationFactory.cs new file mode 100644 index 000000000000..e18f51b1a822 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestWebApplicationFactory.cs @@ -0,0 +1,33 @@ +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using todo_group.Data; + +namespace MinApiTests.IntegrationTests.Helpers +{ + public class TestWebApplicationFactory + : WebApplicationFactory where TProgram : class + { + protected override IHost CreateHost(IHostBuilder builder) + { + builder.ConfigureServices(services => + { + var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions)); + + if (descriptor != null) + { + services.Remove(descriptor); + } + + services.AddDbContext(options => + { + var path = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + options.UseSqlite($"Data Source={Path.Join(path, "todo_group_tests.db")}"); + }); + }); + + return base.CreateHost(builder); + } + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs new file mode 100644 index 000000000000..ee6ad7bebfa8 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs @@ -0,0 +1,77 @@ +using System.Net; +using System.Net.Http.Json; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using MinApiTests.IntegrationTests.Helpers; +using todo_group.Data; + +namespace MinApiTests.IntegrationTests +{ + [Collection("Sequential")] + public class TodoIntegrationTestsV1 : IClassFixture> + { + private readonly TestWebApplicationFactory _factory; + public readonly HttpClient _httpClient; + + public TodoIntegrationTestsV1(TestWebApplicationFactory factory) + { + _factory = factory; + _httpClient = factory.CreateClient(); + } + + public static IEnumerable InvalidTodos => new List + { + new object[] { new TodoDto() { Title = "", Description = "Test description", IsDone = false }, "Name is empty" }, + new object[] { new TodoDto() { Title = "no", Description = "Test description", IsDone = false }, "Name length < 3" } + }; + + [Theory] + [MemberData(nameof(InvalidTodos))] + public async Task PostTodoWithValidationProblems(TodoDto todo, string errorMessage) + { + var response = await _httpClient.PostAsJsonAsync("/todos/v1", todo); + + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + + var problemResult = await response.Content.ReadFromJsonAsync(); + + Assert.NotNull(problemResult?.Errors); + Assert.Collection(problemResult?.Errors, (error) => Assert.Equal(errorMessage, error.Value.First())); + } + + [Fact] + public async Task PostTodoWithValidParametes() + { + using (var scope = _factory.Services.CreateScope()) + { + var db = scope.ServiceProvider.GetService(); + if (db != null && db.Todos.Any()) + { + db.Todos.RemoveRange(db.Todos); + db.SaveChanges(); + } + } + + var response = await _httpClient.PostAsJsonAsync("/todos/v1", new TodoDto + { + Title = "Test title", + Description = "Test description", + IsDone = false + }); + + Assert.Equal(HttpStatusCode.Created, response.StatusCode); + + var todos = await _httpClient.GetFromJsonAsync>("/todos/v1"); + + Assert.NotNull(todos); + Assert.Single(todos); + + Assert.Collection(todos, (todo) => + { + Assert.Equal("Test title", todo.Title); + Assert.Equal("Test description", todo.Description); + Assert.False(todo.IsDone); + }); + } + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs new file mode 100644 index 000000000000..6df4e67da62c --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs @@ -0,0 +1,87 @@ +using System.Net; +using System.Net.Http.Json; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using MinApiTests.IntegrationTests.Helpers; +using todo_group.Data; +using todo_group.Services; + +namespace MinApiTests.IntegrationTests +{ + [Collection("Sequential")] + public class TodoIntegrationTestsV2 : IClassFixture> + { + private readonly TestWebApplicationFactory _factory; + public readonly HttpClient _httpClient; + + public TodoIntegrationTestsV2(TestWebApplicationFactory factory) + { + _factory = factory; + _httpClient = factory.CreateClient(); + } + + public static IEnumerable InvalidTodos => new List + { + new object[] { new TodoDto() { Title = "", Description = "Test description", IsDone = false }, "Name is empty" }, + new object[] { new TodoDto() { Title = "no", Description = "Test description", IsDone = false }, "Name length < 3" } + }; + + [Theory] + [MemberData(nameof(InvalidTodos))] + public async Task PostTodoWithValidationProblems(TodoDto todo, string errorMessage) + { + var response = await _httpClient.PostAsJsonAsync("/todos/v2", todo); + + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + + var problemResult = await response.Content.ReadFromJsonAsync(); + + Assert.NotNull(problemResult?.Errors); + Assert.Collection(problemResult?.Errors, (error) => Assert.Equal(errorMessage, error.Value.First())); + } + + [Fact] + public async Task PostTodoWithValidParametes() + { + using (var scope = _factory.Services.CreateScope()) + { + var db = scope.ServiceProvider.GetService(); + if (db != null && db.Todos.Any()) + { + db.Todos.RemoveRange(db.Todos); + db.SaveChanges(); + } + } + + var client = _factory.WithWebHostBuilder(builder => + { + builder.ConfigureTestServices(services => + { + services.AddSingleton(); + }); + }).CreateClient(); + + var response = await client.PostAsJsonAsync("/todos/v2", new TodoDto + { + Title = "Test title", + Description = "Test description", + IsDone = false + }); + + Assert.Equal(HttpStatusCode.Created, response.StatusCode); + + var todos = await client.GetFromJsonAsync>("/todos/v2"); + + Assert.NotNull(todos); + Assert.Single(todos); + + Assert.Collection(todos, (todo) => + { + Assert.Equal("Test title", todo.Title); + Assert.Equal("Test description", todo.Description); + Assert.False(todo.IsDone); + }); + } + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj index b1c9ddc1b90f..20e1a4f46a90 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj @@ -9,6 +9,7 @@ + diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/Helpers/MockDb.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/Helpers/MockDb.cs new file mode 100644 index 000000000000..d2917218e8d9 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/Helpers/MockDb.cs @@ -0,0 +1,14 @@ +using Microsoft.EntityFrameworkCore; +using todo_group.Data; + +public class MockDb : IDbContextFactory +{ + public TodoGroupDbContext CreateDbContext() + { + var options = new DbContextOptionsBuilder() + .UseInMemoryDatabase($"InMemoryTestDb-{DateTime.Now.ToFileTimeUtc()}") + .Options; + + return new TodoGroupDbContext(options); + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoUnitTestsV1.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoInMemoryTests.cs similarity index 77% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoUnitTestsV1.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoInMemoryTests.cs index 9c6a4bd7b010..8e60e959cac9 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoUnitTestsV1.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoInMemoryTests.cs @@ -1,21 +1,16 @@ using Microsoft.AspNetCore.Http.HttpResults; -using Microsoft.EntityFrameworkCore; -using todo_group.Data; using todo_group; +using todo_group.Data; -namespace MinApiTests +namespace MinApiTests.UnitTests { - public class TodoUnitTestsV1 + public class TodoInMemoryTests { [Fact] public async Task GetTodoReturnsTodoFromDatabase() { // Arrange - var options = new DbContextOptionsBuilder() - .UseInMemoryDatabase($"InMemoryTestDb-{DateTime.Now}") - .Options; - - await using var context = new TodoGroupDbContext(options); + await using var context = new MockDb().CreateDbContext(); context.Todos.Add(new Todo { @@ -43,11 +38,7 @@ public async Task GetTodoReturnsTodoFromDatabase() public async Task CreateTodoCreatesTodoInDatabase() { //Arrange - var options = new DbContextOptionsBuilder() - .UseInMemoryDatabase($"InMemoryTestDb-{DateTime.Now}") - .Options; - - await using var context = new TodoGroupDbContext(options); + await using var context = new MockDb().CreateDbContext(); var newTodo = new TodoDto { @@ -77,11 +68,7 @@ public async Task CreateTodoCreatesTodoInDatabase() public async Task UpdateTodoUpdatesTodoInDatabase() { //Arrange - var options = new DbContextOptionsBuilder() - .UseInMemoryDatabase($"InMemoryTestDb-{DateTime.Now}") - .Options; - - await using var context = new TodoGroupDbContext(options); + await using var context = new MockDb().CreateDbContext(); context.Todos.Add(new Todo { @@ -89,16 +76,16 @@ public async Task UpdateTodoUpdatesTodoInDatabase() Title = "Exiting test title", IsDone = false }); - + await context.SaveChangesAsync(); - + var updatedTodo = new Todo { Id = 1, Title = "Updated test title", IsDone = true }; - + //Act var createdResult = (Created)await TodoEndpointsV1.UpdateTodo(updatedTodo, context); var notFoundResult = (NotFound)await TodoEndpointsV1.UpdateTodo(new Todo { Id = 2, Title = "Invalid Title" }, context); @@ -128,11 +115,7 @@ public async Task DeleteTodoDeletesTodoInDatabase() IsDone = false }; - var options = new DbContextOptionsBuilder() - .UseInMemoryDatabase($"InMemoryTestDb-{DateTime.Now}") - .Options; - - await using var context = new TodoGroupDbContext(options); + await using var context = new MockDb().CreateDbContext(); context.Todos.Add(existingTodo); diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoUnitTestsV2.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoMoqTests.cs similarity index 98% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoUnitTestsV2.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoMoqTests.cs index 892521f4e93f..af1557bfef72 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/TodoUnitTestsV2.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoMoqTests.cs @@ -4,9 +4,9 @@ using todo_group.Data; using todo_group.Services; -namespace MinApiTests; +namespace MinApiTests.UnitTests; -public class TodoUnitTestsV2 +public class TodoMoqTests { [Fact] public async Task GetTodoReturnsTodoFromDatabase() diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoGroupDbContext.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoGroupDbContext.cs index 098be78e506c..9d50e7975be6 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoGroupDbContext.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoGroupDbContext.cs @@ -6,9 +6,8 @@ public class TodoGroupDbContext : DbContext { public DbSet Todos => Set(); - public TodoGroupDbContext(DbContextOptions options) + public TodoGroupDbContext(DbContextOptions options) : base(options) { - } } diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/20220821140431_InitialCreate.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/20220821140431_InitialCreate.cs index 741122637a54..9cc470923859 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/20220821140431_InitialCreate.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/20220821140431_InitialCreate.cs @@ -1,4 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs index f81175ab6f45..ff3692c639b7 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs @@ -5,7 +5,12 @@ var builder = WebApplication.CreateBuilder(args); +builder.Services.AddAuthorization(o => o.AddPolicy("AdminsOnly", + b => b.RequireClaim("admin", "true"))); + builder.Services.AddTransient(); +builder.Services.AddSingleton(); + builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddDbContext(options => @@ -20,6 +25,7 @@ var db = scope.ServiceProvider.GetService(); db?.Database.MigrateAsync(); +app.UseAuthorization(); if (app.Environment.IsDevelopment()) { @@ -29,6 +35,8 @@ } app.MapGet("/", () => "Hello World!"); +app.MapGet("/admin", () => "Authorized Endpoint") + .RequireAuthorization("AdminsOnly"); ; // todoV1 endpoints var todosV1 = app.MapGroup("/todos/v1") @@ -40,6 +48,7 @@ }); todosV1.MapGet("/", TodoEndpointsV1.GetAllTodos); + todosV1.MapGet("/{id}", TodoEndpointsV1.GetTodo); todosV1.MapPost("/", TodoEndpointsV1.CreateTodo) @@ -89,6 +98,7 @@ }); todosV2.MapGet("/", TodoEndpointsV2.GetAllTodos); +todosV2.MapGet("/incompleted", TodoEndpointsV2.GetAllIncompletedTodos); todosV2.MapGet("/{id}", TodoEndpointsV2.GetTodo); todosV2.MapPost("/", TodoEndpointsV2.CreateTodo) @@ -129,3 +139,6 @@ todosV2.MapDelete("/{id}", TodoEndpointsV2.DeleteTodo); app.Run(); + +public partial class Program +{ } diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/EmailService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/EmailService.cs new file mode 100644 index 000000000000..1da8d7cff8de --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/EmailService.cs @@ -0,0 +1,11 @@ +namespace todo_group.Services +{ + public class EmailService : IEmailService + { + public Task Send(string emaidAddress, string body) + { + // Code for sending mails to a configured host + return Task.CompletedTask; + } + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/IEmailService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/IEmailService.cs new file mode 100644 index 000000000000..42a89d77de3b --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/IEmailService.cs @@ -0,0 +1,7 @@ +namespace todo_group.Services +{ + public interface IEmailService + { + Task Send(string emaidAddress, string body); + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/ITodoService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/ITodoService.cs index 53ea16a2c640..3951889b1595 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/ITodoService.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/ITodoService.cs @@ -6,6 +6,8 @@ public interface ITodoService { Task> GetAll(); + Task> GetIncompleteTodos(); + ValueTask Find(int id); Task Add(Todo todo); diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/TodoService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/TodoService.cs index aa5921192215..ca5798ebcf3f 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/TodoService.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/TodoService.cs @@ -6,10 +6,12 @@ namespace todo_group.Services public class TodoService : ITodoService { private readonly TodoGroupDbContext _dbContext; + private readonly IEmailService _emailService; - public TodoService(TodoGroupDbContext dbContext) + public TodoService(TodoGroupDbContext dbContext, IEmailService emailService) { _dbContext = dbContext; + _emailService = emailService; } public async ValueTask Find(int id) @@ -25,7 +27,9 @@ public async Task> GetAll() public async Task Add(Todo todo) { await _dbContext.Todos.AddAsync(todo); - await _dbContext.SaveChangesAsync(); + + if (await _dbContext.SaveChangesAsync() > 0) + await _emailService.Send("hello@microsoft.com", $"New todo has been added: {todo.Title}"); } public async Task Update(Todo todo) @@ -39,5 +43,10 @@ public async Task Remove(Todo todo) _dbContext.Todos.Remove(todo); await _dbContext.SaveChangesAsync(); } + + public Task> GetIncompleteTodos() + { + return _dbContext.Todos.Where(t => t.IsDone == false).ToListAsync(); + } } } diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV2.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV2.cs index 3676cdb42ba0..e8f2606208a4 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV2.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV2.cs @@ -12,6 +12,12 @@ public static async Task GetAllTodos(ITodoService todoService) return TypedResults.Ok(todos); } + public static async Task GetAllIncompletedTodos(ITodoService todoService) + { + var todos = await todoService.GetIncompleteTodos(); + return TypedResults.Ok(todos); + } + // get todo by id public static async Task GetTodo(int id, ITodoService todoService) { diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Utilities.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Utilities.cs index 935655a7fac7..e33b9dd94a5d 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Utilities.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Utilities.cs @@ -10,12 +10,12 @@ public static Dictionary IsValid(TodoDto td) if (string.IsNullOrEmpty(td.Title)) { - errors.Add("todo.name.errors", new[] { "Name is empty" }); + errors.TryAdd("todo.name.errors", new[] { "Name is empty" }); } if (td.Title!.Length < 3) { - errors.Add("todo.name.errors", new[] { "Name length < 3" }); + errors.TryAdd("todo.name.errors", new[] { "Name length < 3" }); } return errors; diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/todo-group.csproj b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/todo-group.csproj index 7a488c9bdb97..8be641a93de9 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/todo-group.csproj +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/todo-group.csproj @@ -6,7 +6,7 @@ enable todo_group - + From 58844f7e864953148c24ce8bb115444a8d1bba52 Mon Sep 17 00:00:00 2001 From: Fiyaz Bin Hasan Date: Tue, 23 Aug 2022 02:53:46 +0600 Subject: [PATCH 08/18] Route group with RouteGroupBuilder --- .../7.0-samples/todo-group/Program.cs | 100 ++---------------- .../7.0-samples/todo-group/TodoEndpointsV1.cs | 25 ++++- .../7.0-samples/todo-group/TodoEndpointsV2.cs | 27 ++++- 3 files changed, 60 insertions(+), 92 deletions(-) diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs index ff3692c639b7..d08b2e06def1 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs @@ -39,57 +39,18 @@ .RequireAuthorization("AdminsOnly"); ; // todoV1 endpoints -var todosV1 = app.MapGroup("/todos/v1") - .WithTags("Todo Endpoints") - .AddEndpointFilter(async (context, next) => - { - app.Logger.LogInformation("Accessing todo endpoints"); - return await next(context); - }); - -todosV1.MapGet("/", TodoEndpointsV1.GetAllTodos); - -todosV1.MapGet("/{id}", TodoEndpointsV1.GetTodo); - -todosV1.MapPost("/", TodoEndpointsV1.CreateTodo) - .AddEndpointFilter(async (context, next) => - { - // log time taken to process - var start = DateTime.Now; - var result = await next(context); - var end = DateTime.Now; - app.Logger.LogInformation($"{context.HttpContext.Request.Path.Value} took {(end - start).TotalMilliseconds}ms"); - return result; - }) - .AddEndpointFilter(async (efiContext, next) => - { - var param = efiContext.GetArgument(0); - - var validationErrors = Utilities.IsValid(param); - - if (validationErrors.Any()) - { - return Results.ValidationProblem(validationErrors); - } - - return await next(efiContext); - }); - -todosV1.MapPut("/{id}", TodoEndpointsV1.UpdateTodo) - .AddEndpointFilter(async (context, next) => - { - // log time taken to process - var start = DateTime.Now; - var result = await next(context); - var end = DateTime.Now; - app.Logger.LogInformation($"{context.HttpContext.Request.Path.Value} took {(end - start).TotalMilliseconds}ms"); - return result; - }); - -todosV1.MapDelete("/{id}", TodoEndpointsV1.DeleteTodo); +app.MapGroup("/todos/v1") + .MapTodosApiV1() + .WithTags("Todo Endpoints") + .AddEndpointFilter(async (context, next) => + { + app.Logger.LogInformation("Accessing todo endpoints"); + return await next(context); + }); // todoV2 endpoints -var todosV2 = app.MapGroup("/todos/v2") +app.MapGroup("/todos/v2") + .MapTodosApiV2() .WithTags("Todo Endpoints") .AddEndpointFilter(async (context, next) => { @@ -97,47 +58,6 @@ return await next(context); }); -todosV2.MapGet("/", TodoEndpointsV2.GetAllTodos); -todosV2.MapGet("/incompleted", TodoEndpointsV2.GetAllIncompletedTodos); -todosV2.MapGet("/{id}", TodoEndpointsV2.GetTodo); - -todosV2.MapPost("/", TodoEndpointsV2.CreateTodo) - .AddEndpointFilter(async (context, next) => - { - // log time taken to process - var start = DateTime.Now; - var result = await next(context); - var end = DateTime.Now; - app.Logger.LogInformation($"{context.HttpContext.Request.Path.Value} took {(end - start).TotalMilliseconds}ms"); - return result; - }) - .AddEndpointFilter(async (efiContext, next) => - { - var param = efiContext.GetArgument(0); - - var validationErrors = Utilities.IsValid(param); - - if (validationErrors.Any()) - { - return Results.ValidationProblem(validationErrors); - } - - return await next(efiContext); - }); - -todosV2.MapPut("/{id}", TodoEndpointsV2.UpdateTodo) - .AddEndpointFilter(async (context, next) => - { - // log time taken to process - var start = DateTime.Now; - var result = await next(context); - var end = DateTime.Now; - app.Logger.LogInformation($"{context.HttpContext.Request.Path.Value} took {(end - start).TotalMilliseconds}ms"); - return result; - }); - -todosV2.MapDelete("/{id}", TodoEndpointsV2.DeleteTodo); - app.Run(); public partial class Program diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV1.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV1.cs index 05b4dec1feb7..9b8e80e2447b 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV1.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV1.cs @@ -3,8 +3,31 @@ namespace todo_group { - public class TodoEndpointsV1 + public static class TodoEndpointsV1 { + public static RouteGroupBuilder MapTodosApiV1(this RouteGroupBuilder group) + { + group.MapGet("/", GetAllTodos); + group.MapGet("/{id}", GetTodo); + group.MapPost("/", CreateTodo) + .AddEndpointFilter(async (efiContext, next) => + { + var param = efiContext.GetArgument(0); + + var validationErrors = Utilities.IsValid(param); + + if (validationErrors.Any()) + { + return Results.ValidationProblem(validationErrors); + } + + return await next(efiContext); + }); + + group.MapPut("/{id}", UpdateTodo); + return group; + } + // get all todos public static async Task GetAllTodos(TodoGroupDbContext database) { diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV2.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV2.cs index e8f2606208a4..b76b584d15b8 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV2.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV2.cs @@ -3,8 +3,33 @@ namespace todo_group; -public class TodoEndpointsV2 +public static class TodoEndpointsV2 { + public static RouteGroupBuilder MapTodosApiV2(this RouteGroupBuilder group) + { + group.MapGet("/", GetAllTodos); + group.MapGet("/incompleted", GetAllIncompletedTodos); + group.MapGet("/{id}", GetTodo); + + group.MapPost("/", CreateTodo) + .AddEndpointFilter(async (efiContext, next) => + { + var param = efiContext.GetArgument(0); + + var validationErrors = Utilities.IsValid(param); + + if (validationErrors.Any()) + { + return Results.ValidationProblem(validationErrors); + } + + return await next(efiContext); + }); + + group.MapPut("/{id}", UpdateTodo); + return group; + } + // get all todos public static async Task GetAllTodos(ITodoService todoService) { From e03d89034a46d14735318d6973f5470ee60968b8 Mon Sep 17 00:00:00 2001 From: Fiyaz Bin Hasan Date: Wed, 14 Sep 2022 12:30:31 +0600 Subject: [PATCH 09/18] Update aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs Co-authored-by: Bruno Oliveira --- .../MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs index ee6ad7bebfa8..b5e369635ce2 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs @@ -40,7 +40,7 @@ public async Task PostTodoWithValidationProblems(TodoDto todo, string errorMessa } [Fact] - public async Task PostTodoWithValidParametes() + public async Task PostTodoWithValidParameters() { using (var scope = _factory.Services.CreateScope()) { From c7eb03314698c40cd89511cf5320be45c9424683 Mon Sep 17 00:00:00 2001 From: Fiyaz Bin Hasan Date: Wed, 14 Sep 2022 12:30:46 +0600 Subject: [PATCH 10/18] Update aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs Co-authored-by: Bruno Oliveira --- .../MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs index 6df4e67da62c..60c3c6f5b5f7 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs @@ -42,7 +42,7 @@ public async Task PostTodoWithValidationProblems(TodoDto todo, string errorMessa } [Fact] - public async Task PostTodoWithValidParametes() + public async Task PostTodoWithValidParameters() { using (var scope = _factory.Services.CreateScope()) { From 36c0db3e7bb11c19ebe1cbb6e1bf9c8903cf64a0 Mon Sep 17 00:00:00 2001 From: Fiyaz Bin Hasan Date: Wed, 14 Sep 2022 13:58:38 +0600 Subject: [PATCH 11/18] namespace update --- .../AuthorizedEndpointsIntegrationTests.cs | 67 +++--- .../Helpers/TestAuthHandler.cs | 41 ++-- .../Helpers/TestEmailService.cs | 15 +- .../Helpers/TestWebApplicationFactory.cs | 37 ++-- .../TodoIntegrationTestsV1.cs | 105 +++++---- .../TodoIntegrationTestsV2.cs | 119 +++++----- .../MinApiTests/MinApiTests.csproj | 2 +- .../MinApiTests/UnitTests/Helpers/MockDb.cs | 8 +- .../UnitTests/TodoInMemoryTests.cs | 208 +++++++++--------- .../MinApiTests/UnitTests/TodoMoqTests.cs | 6 +- .../Data/Todo.cs | 2 +- .../Data/TodoDto.cs | 2 +- .../Data/TodoGroupDbContext.cs | 2 +- .../20220821140431_InitialCreate.Designer.cs | 6 +- .../20220821140431_InitialCreate.cs | 2 +- .../TodoGroupDbContextModelSnapshot.cs | 6 +- .../Program.cs | 25 +-- .../WebMinRouteGroup/Services/EmailService.cs | 10 + .../Services/IEmailService.cs | 6 + .../WebMinRouteGroup/Services/ITodoService.cs | 18 ++ .../WebMinRouteGroup/Services/TodoService.cs | 51 +++++ .../WebMinRouteGroup/TodoEndpointsV1.cs | 103 +++++++++ .../TodoEndpointsV2.cs | 12 +- .../7.0-samples/WebMinRouteGroup/Utilities.cs | 23 ++ .../WebMinRouteGroup.csproj} | 2 +- .../appsettings.Development.json | 0 .../appsettings.json | 0 .../todo-group/Services/EmailService.cs | 11 - .../todo-group/Services/IEmailService.cs | 7 - .../todo-group/Services/ITodoService.cs | 19 -- .../todo-group/Services/TodoService.cs | 52 ----- .../7.0-samples/todo-group/TodoEndpointsV1.cs | 102 --------- .../7.0-samples/todo-group/Utilities.cs | 24 -- 33 files changed, 539 insertions(+), 554 deletions(-) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{todo-group => WebMinRouteGroup}/Data/Todo.cs (86%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{todo-group => WebMinRouteGroup}/Data/TodoDto.cs (84%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{todo-group => WebMinRouteGroup}/Data/TodoGroupDbContext.cs (88%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{todo-group => WebMinRouteGroup}/Migrations/20220821140431_InitialCreate.Designer.cs (90%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{todo-group => WebMinRouteGroup}/Migrations/20220821140431_InitialCreate.cs (96%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{todo-group => WebMinRouteGroup}/Migrations/TodoGroupDbContextModelSnapshot.cs (89%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{todo-group => WebMinRouteGroup}/Program.cs (68%) create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/EmailService.cs create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/IEmailService.cs create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/ITodoService.cs create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/TodoService.cs create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/TodoEndpointsV1.cs rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{todo-group => WebMinRouteGroup}/TodoEndpointsV2.cs (89%) create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Utilities.cs rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{todo-group/todo-group.csproj => WebMinRouteGroup/WebMinRouteGroup.csproj} (95%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{todo-group => WebMinRouteGroup}/appsettings.Development.json (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{todo-group => WebMinRouteGroup}/appsettings.json (100%) delete mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/EmailService.cs delete mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/IEmailService.cs delete mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/ITodoService.cs delete mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/TodoService.cs delete mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV1.cs delete mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Utilities.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/AuthorizedEndpointsIntegrationTests.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/AuthorizedEndpointsIntegrationTests.cs index 7b3e66b5b411..6d8c72f78bd6 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/AuthorizedEndpointsIntegrationTests.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/AuthorizedEndpointsIntegrationTests.cs @@ -4,45 +4,44 @@ using Microsoft.Extensions.DependencyInjection; using MinApiTests.IntegrationTests.Helpers; -namespace MinApiTests.IntegrationTests +namespace MinApiTests.IntegrationTests; + +public class AuthorizedEndpointsIntegrationTests : IClassFixture> { - public class AuthorizedEndpointsIntegrationTests : IClassFixture> - { - private readonly TestWebApplicationFactory _factory; + private readonly TestWebApplicationFactory _factory; - public AuthorizedEndpointsIntegrationTests(TestWebApplicationFactory factory) - { - _factory = factory; - } + public AuthorizedEndpointsIntegrationTests(TestWebApplicationFactory factory) + { + _factory = factory; + } - public static IEnumerable AdminFlags => new List - { - new object[] { "false", HttpStatusCode.Forbidden }, - new object[] { "true", HttpStatusCode.OK } - }; + public static IEnumerable AdminFlags => new List + { + new object[] { "false", HttpStatusCode.Forbidden }, + new object[] { "true", HttpStatusCode.OK } + }; - [Theory] - [MemberData(nameof(AdminFlags))] - public async Task GetAdminEndpointIsReturnedForAnAuthorizedRequest(string isAdmin, HttpStatusCode code) + [Theory] + [MemberData(nameof(AdminFlags))] + public async Task GetAdminEndpointIsReturnedForAnAuthorizedRequest(string isAdmin, HttpStatusCode code) + { + // Arrange + var client = _factory.WithWebHostBuilder(builder => { - // Arrange - var client = _factory.WithWebHostBuilder(builder => + builder.ConfigureTestServices(services => { - builder.ConfigureTestServices(services => - { - services.AddAuthentication("Test") - .AddScheme("Test", - options => options.IsAdmin = isAdmin); - }); - }).CreateClient(); - - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Test"); - - //Act - var response = await client.GetAsync("/admin"); - - // Assert - Assert.Equal(code, response.StatusCode); - } + services.AddAuthentication("Test") + .AddScheme("Test", + options => options.IsAdmin = isAdmin); + }); + }).CreateClient(); + + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Test"); + + //Act + var response = await client.GetAsync("/admin"); + + // Assert + Assert.Equal(code, response.StatusCode); } } diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestAuthHandler.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestAuthHandler.cs index c994d59a445b..b2ec0fe37e5c 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestAuthHandler.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestAuthHandler.cs @@ -4,35 +4,34 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace MinApiTests.IntegrationTests.Helpers +namespace MinApiTests.IntegrationTests.Helpers; + +public class TestAuthenticationSchemeOptions : AuthenticationSchemeOptions +{ + public string IsAdmin { get; set; } = "false"; +} + +public class TestAuthHandler : AuthenticationHandler { - public class TestAuthenticationSchemeOptions : AuthenticationSchemeOptions + public TestAuthHandler(IOptionsMonitor options, + ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + : base(options, logger, encoder, clock) { - public string IsAdmin { get; set; } = "false"; } - public class TestAuthHandler : AuthenticationHandler + protected override Task HandleAuthenticateAsync() { - public TestAuthHandler(IOptionsMonitor options, - ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) - : base(options, logger, encoder, clock) - { - } - - protected override Task HandleAuthenticateAsync() + var claims = new[] { - var claims = new[] - { - new Claim("admin", Options.IsAdmin) - }; + new Claim("admin", Options.IsAdmin) + }; - var identity = new ClaimsIdentity(claims, "Test"); - var principal = new ClaimsPrincipal(identity); - var ticket = new AuthenticationTicket(principal, "Test"); + var identity = new ClaimsIdentity(claims, "Test"); + var principal = new ClaimsPrincipal(identity); + var ticket = new AuthenticationTicket(principal, "Test"); - var result = AuthenticateResult.Success(ticket); + var result = AuthenticateResult.Success(ticket); - return Task.FromResult(result); - } + return Task.FromResult(result); } } diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestEmailService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestEmailService.cs index 85608d6028f8..09005956ab1d 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestEmailService.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestEmailService.cs @@ -1,13 +1,12 @@ -using todo_group.Services; +using WebMinRouteGroup.Services; -namespace MinApiTests.IntegrationTests.Helpers +namespace MinApiTests.IntegrationTests.Helpers; + +public class TestEmailService : IEmailService { - public class TestEmailService : IEmailService + public Task Send(string emailAddress, string body) { - public Task Send(string emaidAddress, string body) - { - // You don't want to send real email when running integration tests - return Task.CompletedTask; - } + // You don't want to send real email when running integration tests + return Task.CompletedTask; } } diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestWebApplicationFactory.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestWebApplicationFactory.cs index e18f51b1a822..8671dd103fc2 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestWebApplicationFactory.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestWebApplicationFactory.cs @@ -2,32 +2,31 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using todo_group.Data; +using WebMinRouteGroup.Data; -namespace MinApiTests.IntegrationTests.Helpers +namespace MinApiTests.IntegrationTests.Helpers; + +public class TestWebApplicationFactory + : WebApplicationFactory where TProgram : class { - public class TestWebApplicationFactory - : WebApplicationFactory where TProgram : class + protected override IHost CreateHost(IHostBuilder builder) { - protected override IHost CreateHost(IHostBuilder builder) + builder.ConfigureServices(services => { - builder.ConfigureServices(services => - { - var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions)); + var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions)); - if (descriptor != null) - { - services.Remove(descriptor); - } + if (descriptor != null) + { + services.Remove(descriptor); + } - services.AddDbContext(options => - { - var path = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); - options.UseSqlite($"Data Source={Path.Join(path, "todo_group_tests.db")}"); - }); + services.AddDbContext(options => + { + var path = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + options.UseSqlite($"Data Source={Path.Join(path, "WebMinRouteGroup_tests.db")}"); }); + }); - return base.CreateHost(builder); - } + return base.CreateHost(builder); } } diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs index b5e369635ce2..38ef6d939866 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs @@ -3,75 +3,74 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using MinApiTests.IntegrationTests.Helpers; -using todo_group.Data; +using WebMinRouteGroup.Data; -namespace MinApiTests.IntegrationTests +namespace MinApiTests.IntegrationTests; + +[Collection("Sequential")] +public class TodoIntegrationTestsV1 : IClassFixture> { - [Collection("Sequential")] - public class TodoIntegrationTestsV1 : IClassFixture> - { - private readonly TestWebApplicationFactory _factory; - public readonly HttpClient _httpClient; + private readonly TestWebApplicationFactory _factory; + private readonly HttpClient _httpClient; - public TodoIntegrationTestsV1(TestWebApplicationFactory factory) - { - _factory = factory; - _httpClient = factory.CreateClient(); - } + public TodoIntegrationTestsV1(TestWebApplicationFactory factory) + { + _factory = factory; + _httpClient = factory.CreateClient(); + } - public static IEnumerable InvalidTodos => new List - { - new object[] { new TodoDto() { Title = "", Description = "Test description", IsDone = false }, "Name is empty" }, - new object[] { new TodoDto() { Title = "no", Description = "Test description", IsDone = false }, "Name length < 3" } - }; + public static IEnumerable InvalidTodos => new List + { + new object[] { new TodoDto { Title = "", Description = "Test description", IsDone = false }, "Name is empty" }, + new object[] { new TodoDto { Title = "no", Description = "Test description", IsDone = false }, "Name length < 3" } + }; - [Theory] - [MemberData(nameof(InvalidTodos))] - public async Task PostTodoWithValidationProblems(TodoDto todo, string errorMessage) - { - var response = await _httpClient.PostAsJsonAsync("/todos/v1", todo); + [Theory] + [MemberData(nameof(InvalidTodos))] + public async Task PostTodoWithValidationProblems(TodoDto todo, string errorMessage) + { + var response = await _httpClient.PostAsJsonAsync("/todos/v1", todo); - Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); - var problemResult = await response.Content.ReadFromJsonAsync(); + var problemResult = await response.Content.ReadFromJsonAsync(); - Assert.NotNull(problemResult?.Errors); - Assert.Collection(problemResult?.Errors, (error) => Assert.Equal(errorMessage, error.Value.First())); - } + Assert.NotNull(problemResult?.Errors); + Assert.Collection(problemResult?.Errors, (error) => Assert.Equal(errorMessage, error.Value.First())); + } - [Fact] - public async Task PostTodoWithValidParameters() + [Fact] + public async Task PostTodoWithValidParameters() + { + using (var scope = _factory.Services.CreateScope()) { - using (var scope = _factory.Services.CreateScope()) + var db = scope.ServiceProvider.GetService(); + if (db != null && db.Todos.Any()) { - var db = scope.ServiceProvider.GetService(); - if (db != null && db.Todos.Any()) - { - db.Todos.RemoveRange(db.Todos); - db.SaveChanges(); - } + db.Todos.RemoveRange(db.Todos); + await db.SaveChangesAsync(); } + } - var response = await _httpClient.PostAsJsonAsync("/todos/v1", new TodoDto - { - Title = "Test title", - Description = "Test description", - IsDone = false - }); + var response = await _httpClient.PostAsJsonAsync("/todos/v1", new TodoDto + { + Title = "Test title", + Description = "Test description", + IsDone = false + }); - Assert.Equal(HttpStatusCode.Created, response.StatusCode); + Assert.Equal(HttpStatusCode.Created, response.StatusCode); - var todos = await _httpClient.GetFromJsonAsync>("/todos/v1"); + var todos = await _httpClient.GetFromJsonAsync>("/todos/v1"); - Assert.NotNull(todos); - Assert.Single(todos); + Assert.NotNull(todos); + Assert.Single(todos); - Assert.Collection(todos, (todo) => - { - Assert.Equal("Test title", todo.Title); - Assert.Equal("Test description", todo.Description); - Assert.False(todo.IsDone); - }); - } + Assert.Collection(todos, (todo) => + { + Assert.Equal("Test title", todo.Title); + Assert.Equal("Test description", todo.Description); + Assert.False(todo.IsDone); + }); } } diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs index 60c3c6f5b5f7..d5b0ec9d7391 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs @@ -4,84 +4,83 @@ using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using MinApiTests.IntegrationTests.Helpers; -using todo_group.Data; -using todo_group.Services; +using WebMinRouteGroup.Data; +using WebMinRouteGroup.Services; -namespace MinApiTests.IntegrationTests +namespace MinApiTests.IntegrationTests; + +[Collection("Sequential")] +public class TodoIntegrationTestsV2 : IClassFixture> { - [Collection("Sequential")] - public class TodoIntegrationTestsV2 : IClassFixture> - { - private readonly TestWebApplicationFactory _factory; - public readonly HttpClient _httpClient; + private readonly TestWebApplicationFactory _factory; + private readonly HttpClient _httpClient; - public TodoIntegrationTestsV2(TestWebApplicationFactory factory) - { - _factory = factory; - _httpClient = factory.CreateClient(); - } + public TodoIntegrationTestsV2(TestWebApplicationFactory factory) + { + _factory = factory; + _httpClient = factory.CreateClient(); + } - public static IEnumerable InvalidTodos => new List - { - new object[] { new TodoDto() { Title = "", Description = "Test description", IsDone = false }, "Name is empty" }, - new object[] { new TodoDto() { Title = "no", Description = "Test description", IsDone = false }, "Name length < 3" } - }; + public static IEnumerable InvalidTodos => new List + { + new object[] { new TodoDto { Title = "", Description = "Test description", IsDone = false }, "Name is empty" }, + new object[] { new TodoDto { Title = "no", Description = "Test description", IsDone = false }, "Name length < 3" } + }; - [Theory] - [MemberData(nameof(InvalidTodos))] - public async Task PostTodoWithValidationProblems(TodoDto todo, string errorMessage) - { - var response = await _httpClient.PostAsJsonAsync("/todos/v2", todo); + [Theory] + [MemberData(nameof(InvalidTodos))] + public async Task PostTodoWithValidationProblems(TodoDto todo, string errorMessage) + { + var response = await _httpClient.PostAsJsonAsync("/todos/v2", todo); - Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); - var problemResult = await response.Content.ReadFromJsonAsync(); + var problemResult = await response.Content.ReadFromJsonAsync(); - Assert.NotNull(problemResult?.Errors); - Assert.Collection(problemResult?.Errors, (error) => Assert.Equal(errorMessage, error.Value.First())); - } + Assert.NotNull(problemResult?.Errors); + Assert.Collection(problemResult?.Errors, (error) => Assert.Equal(errorMessage, error.Value.First())); + } - [Fact] - public async Task PostTodoWithValidParameters() + [Fact] + public async Task PostTodoWithValidParameters() + { + using (var scope = _factory.Services.CreateScope()) { - using (var scope = _factory.Services.CreateScope()) + var db = scope.ServiceProvider.GetService(); + if (db != null && db.Todos.Any()) { - var db = scope.ServiceProvider.GetService(); - if (db != null && db.Todos.Any()) - { - db.Todos.RemoveRange(db.Todos); - db.SaveChanges(); - } + db.Todos.RemoveRange(db.Todos); + db.SaveChanges(); } + } - var client = _factory.WithWebHostBuilder(builder => - { - builder.ConfigureTestServices(services => - { - services.AddSingleton(); - }); - }).CreateClient(); - - var response = await client.PostAsJsonAsync("/todos/v2", new TodoDto + var client = _factory.WithWebHostBuilder(builder => + { + builder.ConfigureTestServices(services => { - Title = "Test title", - Description = "Test description", - IsDone = false + services.AddSingleton(); }); + }).CreateClient(); - Assert.Equal(HttpStatusCode.Created, response.StatusCode); + var response = await client.PostAsJsonAsync("/todos/v2", new TodoDto + { + Title = "Test title", + Description = "Test description", + IsDone = false + }); - var todos = await client.GetFromJsonAsync>("/todos/v2"); + Assert.Equal(HttpStatusCode.Created, response.StatusCode); - Assert.NotNull(todos); - Assert.Single(todos); + var todos = await client.GetFromJsonAsync>("/todos/v2"); - Assert.Collection(todos, (todo) => - { - Assert.Equal("Test title", todo.Title); - Assert.Equal("Test description", todo.Description); - Assert.False(todo.IsDone); - }); - } + Assert.NotNull(todos); + Assert.Single(todos); + + Assert.Collection(todos, (todo) => + { + Assert.Equal("Test title", todo.Title); + Assert.Equal("Test description", todo.Description); + Assert.False(todo.IsDone); + }); } } diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj index 20e1a4f46a90..1f54b253b107 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj @@ -25,7 +25,7 @@ - + diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/Helpers/MockDb.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/Helpers/MockDb.cs index d2917218e8d9..1d086fc608ac 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/Helpers/MockDb.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/Helpers/MockDb.cs @@ -1,13 +1,15 @@ using Microsoft.EntityFrameworkCore; -using todo_group.Data; +using WebMinRouteGroup.Data; + +namespace MinApiTests.UnitTests.Helpers; public class MockDb : IDbContextFactory { public TodoGroupDbContext CreateDbContext() { var options = new DbContextOptionsBuilder() - .UseInMemoryDatabase($"InMemoryTestDb-{DateTime.Now.ToFileTimeUtc()}") - .Options; + .UseInMemoryDatabase($"InMemoryTestDb-{DateTime.Now.ToFileTimeUtc()}") + .Options; return new TodoGroupDbContext(options); } diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoInMemoryTests.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoInMemoryTests.cs index 8e60e959cac9..a5096abe00ea 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoInMemoryTests.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoInMemoryTests.cs @@ -1,135 +1,135 @@ using Microsoft.AspNetCore.Http.HttpResults; -using todo_group; -using todo_group.Data; +using MinApiTests.UnitTests.Helpers; +using WebMinRouteGroup; +using WebMinRouteGroup.Data; -namespace MinApiTests.UnitTests +namespace MinApiTests.UnitTests; + +public class TodoInMemoryTests { - public class TodoInMemoryTests + [Fact] + public async Task GetTodoReturnsTodoFromDatabase() { - [Fact] - public async Task GetTodoReturnsTodoFromDatabase() + // Arrange + await using var context = new MockDb().CreateDbContext(); + + context.Todos.Add(new Todo { - // Arrange - await using var context = new MockDb().CreateDbContext(); + Id = 1, + Title = "Test title", + Description = "Test description", + IsDone = false + }); - context.Todos.Add(new Todo - { - Id = 1, - Title = "Test title", - Description = "Test description", - IsDone = false - }); + await context.SaveChangesAsync(); - await context.SaveChangesAsync(); + // Act + var okResult = (Ok)await TodoEndpointsV1.GetTodo(1, context); + var notFoundResult = (NotFound)await TodoEndpointsV1.GetTodo(404, context); - // Act - var okResult = (Ok)await TodoEndpointsV1.GetTodo(1, context); - var notFoundResult = (NotFound)await TodoEndpointsV1.GetTodo(404, context); + //Assert + Assert.Equal(200, okResult.StatusCode); + var foundTodo = Assert.IsAssignableFrom(okResult.Value); + Assert.Equal(1, foundTodo.Id); - //Assert - Assert.Equal(200, okResult.StatusCode); - var foundTodo = Assert.IsAssignableFrom(okResult.Value); - Assert.Equal(1, foundTodo.Id); + Assert.Equal(404, notFoundResult.StatusCode); + } - Assert.Equal(404, notFoundResult.StatusCode); - } + [Fact] + public async Task CreateTodoCreatesTodoInDatabase() + { + //Arrange + await using var context = new MockDb().CreateDbContext(); - [Fact] - public async Task CreateTodoCreatesTodoInDatabase() + var newTodo = new TodoDto { - //Arrange - await using var context = new MockDb().CreateDbContext(); - - var newTodo = new TodoDto - { - Title = "Test title", - Description = "Test description", - IsDone = false - }; - - //Act - var createdResult = (Created)await TodoEndpointsV1.CreateTodo(newTodo, context); - - //Assert - Assert.Equal(201, createdResult.StatusCode); - Assert.NotNull(createdResult.Location); - Assert.IsAssignableFrom(createdResult.Value); - - Assert.NotEmpty(context.Todos); - Assert.Collection(context.Todos, todo => - { - Assert.Equal("Test title", todo.Title); - Assert.Equal("Test description", todo.Description); - Assert.False(todo.IsDone); - }); - } - - [Fact] - public async Task UpdateTodoUpdatesTodoInDatabase() + Title = "Test title", + Description = "Test description", + IsDone = false + }; + + //Act + var createdResult = (Created)await TodoEndpointsV1.CreateTodo(newTodo, context); + + //Assert + Assert.Equal(201, createdResult.StatusCode); + Assert.NotNull(createdResult.Location); + Assert.IsAssignableFrom(createdResult.Value); + + Assert.NotEmpty(context.Todos); + Assert.Collection(context.Todos, todo => { - //Arrange - await using var context = new MockDb().CreateDbContext(); + Assert.Equal("Test title", todo.Title); + Assert.Equal("Test description", todo.Description); + Assert.False(todo.IsDone); + }); + } - context.Todos.Add(new Todo - { - Id = 1, - Title = "Exiting test title", - IsDone = false - }); + [Fact] + public async Task UpdateTodoUpdatesTodoInDatabase() + { + //Arrange + await using var context = new MockDb().CreateDbContext(); - await context.SaveChangesAsync(); + context.Todos.Add(new Todo + { + Id = 1, + Title = "Exiting test title", + IsDone = false + }); - var updatedTodo = new Todo - { - Id = 1, - Title = "Updated test title", - IsDone = true - }; + await context.SaveChangesAsync(); - //Act - var createdResult = (Created)await TodoEndpointsV1.UpdateTodo(updatedTodo, context); - var notFoundResult = (NotFound)await TodoEndpointsV1.UpdateTodo(new Todo { Id = 2, Title = "Invalid Title" }, context); + var updatedTodo = new Todo + { + Id = 1, + Title = "Updated test title", + IsDone = true + }; - //Assert - Assert.Equal(201, createdResult.StatusCode); - Assert.NotNull(createdResult.Location); - Assert.IsAssignableFrom(createdResult.Value); + //Act + var createdResult = (Created)await TodoEndpointsV1.UpdateTodo(updatedTodo, context); + var notFoundResult = (NotFound)await TodoEndpointsV1.UpdateTodo(new Todo { Id = 2, Title = "Invalid Title" }, context); - Assert.Equal(404, notFoundResult.StatusCode); + //Assert + Assert.Equal(201, createdResult.StatusCode); + Assert.NotNull(createdResult.Location); + Assert.IsAssignableFrom(createdResult.Value); - var todoInDb = await context.Todos.FindAsync(1); + Assert.Equal(404, notFoundResult.StatusCode); - Assert.NotNull(todoInDb); - Assert.Equal("Updated test title", todoInDb!.Title); - Assert.True(todoInDb.IsDone); - } + var todoInDb = await context.Todos.FindAsync(1); - [Fact] - public async Task DeleteTodoDeletesTodoInDatabase() + Assert.NotNull(todoInDb); + Assert.Equal("Updated test title", todoInDb!.Title); + Assert.True(todoInDb.IsDone); + } + + [Fact] + public async Task DeleteTodoDeletesTodoInDatabase() + { + //Arrange + var existingTodo = new Todo() { - //Arrange - var existingTodo = new Todo() - { - Id = 1, - Title = "Exiting test title", - IsDone = false - }; + Id = 1, + Title = "Exiting test title", + IsDone = false + }; - await using var context = new MockDb().CreateDbContext(); + await using var context = new MockDb().CreateDbContext(); - context.Todos.Add(existingTodo); + context.Todos.Add(existingTodo); - await context.SaveChangesAsync(); + await context.SaveChangesAsync(); - //Act - var noContentResult = (NoContent)await TodoEndpointsV1.DeleteTodo(existingTodo.Id, context); - var notFoundResult = (NotFound)await TodoEndpointsV1.DeleteTodo(2, context); + //Act + var noContentResult = (NoContent)await TodoEndpointsV1.DeleteTodo(existingTodo.Id, context); + var notFoundResult = (NotFound)await TodoEndpointsV1.DeleteTodo(2, context); - //Assert - Assert.Equal(204, noContentResult.StatusCode); - Assert.Empty(context.Todos); + //Assert + Assert.Equal(204, noContentResult.StatusCode); + Assert.Empty(context.Todos); - Assert.Equal(404, notFoundResult.StatusCode); - } + Assert.Equal(404, notFoundResult.StatusCode); } } diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoMoqTests.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoMoqTests.cs index af1557bfef72..acc11959b5e6 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoMoqTests.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoMoqTests.cs @@ -1,8 +1,8 @@ using Microsoft.AspNetCore.Http.HttpResults; using Moq; -using todo_group; -using todo_group.Data; -using todo_group.Services; +using WebMinRouteGroup; +using WebMinRouteGroup.Data; +using WebMinRouteGroup.Services; namespace MinApiTests.UnitTests; diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/Todo.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Data/Todo.cs similarity index 86% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/Todo.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Data/Todo.cs index 295125b1b7af..9be2d443790e 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/Todo.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Data/Todo.cs @@ -1,4 +1,4 @@ -namespace todo_group.Data; +namespace WebMinRouteGroup.Data; public class Todo { diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoDto.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Data/TodoDto.cs similarity index 84% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoDto.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Data/TodoDto.cs index e1df4b5ec78a..eda4c49b45c6 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoDto.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Data/TodoDto.cs @@ -1,4 +1,4 @@ -namespace todo_group.Data; +namespace WebMinRouteGroup.Data; public class TodoDto { diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoGroupDbContext.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Data/TodoGroupDbContext.cs similarity index 88% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoGroupDbContext.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Data/TodoGroupDbContext.cs index 9d50e7975be6..64707db7ff35 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoGroupDbContext.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Data/TodoGroupDbContext.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore; -namespace todo_group.Data; +namespace WebMinRouteGroup.Data; public class TodoGroupDbContext : DbContext { diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/20220821140431_InitialCreate.Designer.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Migrations/20220821140431_InitialCreate.Designer.cs similarity index 90% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/20220821140431_InitialCreate.Designer.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Migrations/20220821140431_InitialCreate.Designer.cs index 07ce22dc78fd..af49cd4a19de 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/20220821140431_InitialCreate.Designer.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Migrations/20220821140431_InitialCreate.Designer.cs @@ -3,11 +3,11 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using todo_group.Data; +using WebMinRouteGroup.Data; #nullable disable -namespace todo_group.Migrations +namespace WebMinRouteGroup.Migrations { [DbContext(typeof(TodoGroupDbContext))] [Migration("20220821140431_InitialCreate")] @@ -19,7 +19,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder.HasAnnotation("ProductVersion", "7.0.0-preview.7.22376.2"); - modelBuilder.Entity("todo_group.Data.Todo", b => + modelBuilder.Entity("WebMinRouteGroup.Data.Todo", b => { b.Property("Id") .ValueGeneratedOnAdd() diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/20220821140431_InitialCreate.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Migrations/20220821140431_InitialCreate.cs similarity index 96% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/20220821140431_InitialCreate.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Migrations/20220821140431_InitialCreate.cs index 9cc470923859..48ec7a0def7e 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/20220821140431_InitialCreate.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Migrations/20220821140431_InitialCreate.cs @@ -2,7 +2,7 @@ #nullable disable -namespace todo_group.Migrations +namespace WebMinRouteGroup.Migrations { /// public partial class InitialCreate : Migration diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/TodoGroupDbContextModelSnapshot.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Migrations/TodoGroupDbContextModelSnapshot.cs similarity index 89% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/TodoGroupDbContextModelSnapshot.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Migrations/TodoGroupDbContextModelSnapshot.cs index 0649cb5b07ce..ae12297c8c42 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Migrations/TodoGroupDbContextModelSnapshot.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Migrations/TodoGroupDbContextModelSnapshot.cs @@ -2,11 +2,11 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using todo_group.Data; +using WebMinRouteGroup.Data; #nullable disable -namespace todo_group.Migrations +namespace WebMinRouteGroup.Migrations { [DbContext(typeof(TodoGroupDbContext))] partial class TodoGroupDbContextModelSnapshot : ModelSnapshot @@ -16,7 +16,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder.HasAnnotation("ProductVersion", "7.0.0-preview.7.22376.2"); - modelBuilder.Entity("todo_group.Data.Todo", b => + modelBuilder.Entity("WebMinRouteGroup.Data.Todo", b => { b.Property("Id") .ValueGeneratedOnAdd() diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Program.cs similarity index 68% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Program.cs index d08b2e06def1..dc1c2739b84d 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Program.cs @@ -1,7 +1,7 @@ using Microsoft.EntityFrameworkCore; -using todo_group; -using todo_group.Data; -using todo_group.Services; +using WebMinRouteGroup; +using WebMinRouteGroup.Data; +using WebMinRouteGroup.Services; var builder = WebApplication.CreateBuilder(args); @@ -16,7 +16,7 @@ builder.Services.AddDbContext(options => { var path = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); - options.UseSqlite($"Data Source={Path.Join(path, "todo_group.db")}"); + options.UseSqlite($"Data Source={Path.Join(path, "WebMinRouteGroup.db")}"); }); var app = builder.Build(); @@ -35,28 +35,19 @@ } app.MapGet("/", () => "Hello World!"); + app.MapGet("/admin", () => "Authorized Endpoint") .RequireAuthorization("AdminsOnly"); ; // todoV1 endpoints app.MapGroup("/todos/v1") - .MapTodosApiV1() - .WithTags("Todo Endpoints") - .AddEndpointFilter(async (context, next) => - { - app.Logger.LogInformation("Accessing todo endpoints"); - return await next(context); - }); + .MapTodosApiV1() + .WithTags("Todo Endpoints"); // todoV2 endpoints app.MapGroup("/todos/v2") .MapTodosApiV2() - .WithTags("Todo Endpoints") - .AddEndpointFilter(async (context, next) => - { - app.Logger.LogInformation("Accessing todo endpoints"); - return await next(context); - }); + .WithTags("Todo Endpoints"); app.Run(); diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/EmailService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/EmailService.cs new file mode 100644 index 000000000000..4380edecd61d --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/EmailService.cs @@ -0,0 +1,10 @@ +namespace WebMinRouteGroup.Services; + +public class EmailService : IEmailService +{ + public Task Send(string emailAddress, string body) + { + // Code for sending mails to a configured host + return Task.CompletedTask; + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/IEmailService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/IEmailService.cs new file mode 100644 index 000000000000..55bf3b1d8e6f --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/IEmailService.cs @@ -0,0 +1,6 @@ +namespace WebMinRouteGroup.Services; + +public interface IEmailService +{ + Task Send(string emailAddress, string body); +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/ITodoService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/ITodoService.cs new file mode 100644 index 000000000000..cea30efe735f --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/ITodoService.cs @@ -0,0 +1,18 @@ +using WebMinRouteGroup.Data; + +namespace WebMinRouteGroup.Services; + +public interface ITodoService +{ + Task> GetAll(); + + Task> GetIncompleteTodos(); + + ValueTask Find(int id); + + Task Add(Todo todo); + + Task Update(Todo todo); + + Task Remove(Todo todo); +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/TodoService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/TodoService.cs new file mode 100644 index 000000000000..54689cdb422c --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/TodoService.cs @@ -0,0 +1,51 @@ +using Microsoft.EntityFrameworkCore; +using WebMinRouteGroup.Data; + +namespace WebMinRouteGroup.Services; + +public class TodoService : ITodoService +{ + private readonly TodoGroupDbContext _dbContext; + private readonly IEmailService _emailService; + + public TodoService(TodoGroupDbContext dbContext, IEmailService emailService) + { + _dbContext = dbContext; + _emailService = emailService; + } + + public async ValueTask Find(int id) + { + return await _dbContext.Todos.FindAsync(id); + } + + public async Task> GetAll() + { + return await _dbContext.Todos.ToListAsync(); + } + + public async Task Add(Todo todo) + { + await _dbContext.Todos.AddAsync(todo); + + if (await _dbContext.SaveChangesAsync() > 0) + await _emailService.Send("hello@microsoft.com", $"New todo has been added: {todo.Title}"); + } + + public async Task Update(Todo todo) + { + _dbContext.Todos.Update(todo); + await _dbContext.SaveChangesAsync(); + } + + public async Task Remove(Todo todo) + { + _dbContext.Todos.Remove(todo); + await _dbContext.SaveChangesAsync(); + } + + public Task> GetIncompleteTodos() + { + return _dbContext.Todos.Where(t => t.IsDone == false).ToListAsync(); + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/TodoEndpointsV1.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/TodoEndpointsV1.cs new file mode 100644 index 000000000000..a03b8b24749a --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/TodoEndpointsV1.cs @@ -0,0 +1,103 @@ +using Microsoft.EntityFrameworkCore; +using WebMinRouteGroup.Data; + +namespace WebMinRouteGroup; + +public static class TodoEndpointsV1 +{ + public static RouteGroupBuilder MapTodosApiV1(this RouteGroupBuilder group) + { + group.MapGet("/", GetAllTodos); + group.MapGet("/{id}", GetTodo); + group.MapPost("/", CreateTodo) + .AddEndpointFilter(async (efiContext, next) => + { + var param = efiContext.GetArgument(0); + + var validationErrors = Utilities.IsValid(param); + + if (validationErrors.Any()) + { + return Results.ValidationProblem(validationErrors); + } + + return await next(efiContext); + }); + + group.MapPut("/{id}", UpdateTodo); + group.MapDelete("/{id}", DeleteTodo); + + return group; + } + + // get all todos + public static async Task GetAllTodos(TodoGroupDbContext database) + { + var todos = await database.Todos.ToListAsync(); + return TypedResults.Ok(todos); + } + + // get todo by id + public static async Task GetTodo(int id, TodoGroupDbContext database) + { + var todo = await database.Todos.FindAsync(id); + + if (todo != null) + { + return TypedResults.Ok(todo); + } + + return TypedResults.NotFound(); + } + + // create todo + public static async Task CreateTodo(TodoDto todo, TodoGroupDbContext database) + { + var newTodo = new Todo + { + Title = todo.Title, + Description = todo.Description, + IsDone = todo.IsDone + }; + + await database.Todos.AddAsync(newTodo); + await database.SaveChangesAsync(); + + return TypedResults.Created($"/todos/v1/{newTodo.Id}", newTodo); + } + + // update todo + public static async Task UpdateTodo(Todo todo, TodoGroupDbContext database) + { + var existingTodo = await database.Todos.FindAsync(todo.Id); + + if (existingTodo != null) + { + existingTodo.Title = todo.Title; + existingTodo.Description = todo.Description; + existingTodo.IsDone = todo.IsDone; + + await database.SaveChangesAsync(); + + return TypedResults.Created($"/todos/v1/{existingTodo.Id}", existingTodo); + } + + return TypedResults.NotFound(); + } + + // delete todo + public static async Task DeleteTodo(int id, TodoGroupDbContext database) + { + var todo = await database.Todos.FindAsync(id); + + if (todo != null) + { + database.Todos.Remove(todo); + await database.SaveChangesAsync(); + + return TypedResults.NoContent(); + } + + return TypedResults.NotFound(); + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV2.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/TodoEndpointsV2.cs similarity index 89% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV2.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/TodoEndpointsV2.cs index b76b584d15b8..30c3e5ee9f23 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV2.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/TodoEndpointsV2.cs @@ -1,7 +1,7 @@ -using todo_group.Data; -using todo_group.Services; +using WebMinRouteGroup.Data; +using WebMinRouteGroup.Services; -namespace todo_group; +namespace WebMinRouteGroup; public static class TodoEndpointsV2 { @@ -27,6 +27,8 @@ public static RouteGroupBuilder MapTodosApiV2(this RouteGroupBuilder group) }); group.MapPut("/{id}", UpdateTodo); + group.MapDelete("/{id}", DeleteTodo); + return group; } @@ -68,7 +70,7 @@ public static async Task CreateTodo(TodoDto todo, ITodoService todoServ await todoService.Add(newTodo); - return TypedResults.Created($"/public/todos/{newTodo.Id}", newTodo); + return TypedResults.Created($"/todos/v1/{newTodo.Id}", newTodo); } // update todo @@ -84,7 +86,7 @@ public static async Task UpdateTodo(Todo todo, ITodoService todoService await todoService.Update(existingTodo); - return TypedResults.Created($"/public/todos/{existingTodo.Id}", existingTodo); + return TypedResults.Created($"/todos/v1/{existingTodo.Id}", existingTodo); } return TypedResults.NotFound(); diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Utilities.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Utilities.cs new file mode 100644 index 000000000000..6544f6bb7f91 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Utilities.cs @@ -0,0 +1,23 @@ +using WebMinRouteGroup.Data; + +namespace WebMinRouteGroup; + +public static class Utilities +{ + public static Dictionary IsValid(TodoDto td) + { + Dictionary errors = new(); + + if (string.IsNullOrEmpty(td.Title)) + { + errors.TryAdd("todo.name.errors", new[] { "Name is empty" }); + } + + if (td.Title!.Length < 3) + { + errors.TryAdd("todo.name.errors", new[] { "Name length < 3" }); + } + + return errors; + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/todo-group.csproj b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/WebMinRouteGroup.csproj similarity index 95% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/todo-group.csproj rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/WebMinRouteGroup.csproj index 8be641a93de9..5d31ea2ddcf6 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/todo-group.csproj +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/WebMinRouteGroup.csproj @@ -4,7 +4,7 @@ net7.0 enable enable - todo_group + WebMinRouteGroup diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/appsettings.Development.json b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/appsettings.Development.json similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/appsettings.Development.json rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/appsettings.Development.json diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/appsettings.json b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/appsettings.json similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/appsettings.json rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/appsettings.json diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/EmailService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/EmailService.cs deleted file mode 100644 index 1da8d7cff8de..000000000000 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/EmailService.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace todo_group.Services -{ - public class EmailService : IEmailService - { - public Task Send(string emaidAddress, string body) - { - // Code for sending mails to a configured host - return Task.CompletedTask; - } - } -} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/IEmailService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/IEmailService.cs deleted file mode 100644 index 42a89d77de3b..000000000000 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/IEmailService.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace todo_group.Services -{ - public interface IEmailService - { - Task Send(string emaidAddress, string body); - } -} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/ITodoService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/ITodoService.cs deleted file mode 100644 index 3951889b1595..000000000000 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/ITodoService.cs +++ /dev/null @@ -1,19 +0,0 @@ -using todo_group.Data; - -namespace todo_group.Services -{ - public interface ITodoService - { - Task> GetAll(); - - Task> GetIncompleteTodos(); - - ValueTask Find(int id); - - Task Add(Todo todo); - - Task Update(Todo todo); - - Task Remove(Todo todo); - } -} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/TodoService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/TodoService.cs deleted file mode 100644 index ca5798ebcf3f..000000000000 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Services/TodoService.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using todo_group.Data; - -namespace todo_group.Services -{ - public class TodoService : ITodoService - { - private readonly TodoGroupDbContext _dbContext; - private readonly IEmailService _emailService; - - public TodoService(TodoGroupDbContext dbContext, IEmailService emailService) - { - _dbContext = dbContext; - _emailService = emailService; - } - - public async ValueTask Find(int id) - { - return await _dbContext.Todos.FindAsync(id); - } - - public async Task> GetAll() - { - return await _dbContext.Todos.ToListAsync(); - } - - public async Task Add(Todo todo) - { - await _dbContext.Todos.AddAsync(todo); - - if (await _dbContext.SaveChangesAsync() > 0) - await _emailService.Send("hello@microsoft.com", $"New todo has been added: {todo.Title}"); - } - - public async Task Update(Todo todo) - { - _dbContext.Todos.Update(todo); - await _dbContext.SaveChangesAsync(); - } - - public async Task Remove(Todo todo) - { - _dbContext.Todos.Remove(todo); - await _dbContext.SaveChangesAsync(); - } - - public Task> GetIncompleteTodos() - { - return _dbContext.Todos.Where(t => t.IsDone == false).ToListAsync(); - } - } -} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV1.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV1.cs deleted file mode 100644 index 9b8e80e2447b..000000000000 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/TodoEndpointsV1.cs +++ /dev/null @@ -1,102 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using todo_group.Data; - -namespace todo_group -{ - public static class TodoEndpointsV1 - { - public static RouteGroupBuilder MapTodosApiV1(this RouteGroupBuilder group) - { - group.MapGet("/", GetAllTodos); - group.MapGet("/{id}", GetTodo); - group.MapPost("/", CreateTodo) - .AddEndpointFilter(async (efiContext, next) => - { - var param = efiContext.GetArgument(0); - - var validationErrors = Utilities.IsValid(param); - - if (validationErrors.Any()) - { - return Results.ValidationProblem(validationErrors); - } - - return await next(efiContext); - }); - - group.MapPut("/{id}", UpdateTodo); - return group; - } - - // get all todos - public static async Task GetAllTodos(TodoGroupDbContext database) - { - var todos = await database.Todos.ToListAsync(); - return TypedResults.Ok(todos); - } - - // get todo by id - public static async Task GetTodo(int id, TodoGroupDbContext database) - { - var todo = await database.Todos.FindAsync(id); - - if (todo != null) - { - return TypedResults.Ok(todo); - } - - return TypedResults.NotFound(); - } - - // create todo - public static async Task CreateTodo(TodoDto todo, TodoGroupDbContext database) - { - var newTodo = new Todo - { - Title = todo.Title, - Description = todo.Description, - IsDone = todo.IsDone - }; - - await database.Todos.AddAsync(newTodo); - await database.SaveChangesAsync(); - - return TypedResults.Created($"/public/todos/{newTodo.Id}", newTodo); - } - - // update todo - public static async Task UpdateTodo(Todo todo, TodoGroupDbContext database) - { - var existingTodo = await database.Todos.FindAsync(todo.Id); - - if (existingTodo != null) - { - existingTodo.Title = todo.Title; - existingTodo.Description = todo.Description; - existingTodo.IsDone = todo.IsDone; - - await database.SaveChangesAsync(); - - return TypedResults.Created($"/public/todos/{existingTodo.Id}", existingTodo); - } - - return TypedResults.NotFound(); - } - - // delete todo - public static async Task DeleteTodo(int id, TodoGroupDbContext database) - { - var todo = await database.Todos.FindAsync(id); - - if (todo != null) - { - database.Todos.Remove(todo); - await database.SaveChangesAsync(); - - return TypedResults.NoContent(); - } - - return TypedResults.NotFound(); - } - } -} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Utilities.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Utilities.cs deleted file mode 100644 index e33b9dd94a5d..000000000000 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Utilities.cs +++ /dev/null @@ -1,24 +0,0 @@ -using todo_group.Data; - -namespace todo_group -{ - public static class Utilities - { - public static Dictionary IsValid(TodoDto td) - { - Dictionary errors = new(); - - if (string.IsNullOrEmpty(td.Title)) - { - errors.TryAdd("todo.name.errors", new[] { "Name is empty" }); - } - - if (td.Title!.Length < 3) - { - errors.TryAdd("todo.name.errors", new[] { "Name length < 3" }); - } - - return errors; - } - } -} From aa0a6d927fb181319097ae76729a8a429b7fd2ac Mon Sep 17 00:00:00 2001 From: Fiyaz Bin Hasan Date: Fri, 16 Sep 2022 18:19:17 +0600 Subject: [PATCH 12/18] dotnet 7 rc1 packages update --- .../7.0-samples/MinApiTests/MinApiTests.csproj | 10 +++++----- .../WebMinRouteGroup/WebMinRouteGroup.csproj | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj index 1f54b253b107..a4ba89b23392 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj @@ -9,12 +9,12 @@ - - - + + + - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/WebMinRouteGroup.csproj b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/WebMinRouteGroup.csproj index 5d31ea2ddcf6..04fab39ba53d 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/WebMinRouteGroup.csproj +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/WebMinRouteGroup.csproj @@ -8,13 +8,13 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive From a44aca190b3c051aa3c4628fed87e42309863863 Mon Sep 17 00:00:00 2001 From: Fiyaz Bin Hasan Date: Sat, 17 Sep 2022 03:26:40 +0600 Subject: [PATCH 13/18] common fact to unit test not found result when todo is not inside database --- .../UnitTests/TodoInMemoryTests.cs | 28 ++++++++------- .../MinApiTests/UnitTests/TodoMoqTests.cs | 36 +++++++++---------- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoInMemoryTests.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoInMemoryTests.cs index a5096abe00ea..5403bcce7010 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoInMemoryTests.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoInMemoryTests.cs @@ -7,6 +7,19 @@ namespace MinApiTests.UnitTests; public class TodoInMemoryTests { + [Fact] + public async Task GetTodoReturnsNotFoundIfNotExists() + { + // Arrange + await using var context = new MockDb().CreateDbContext(); + + // Act + var notFoundResult = (NotFound)await TodoEndpointsV1.GetTodo(404, context); + + //Assert + Assert.Equal(404, notFoundResult.StatusCode); + } + [Fact] public async Task GetTodoReturnsTodoFromDatabase() { @@ -25,14 +38,11 @@ public async Task GetTodoReturnsTodoFromDatabase() // Act var okResult = (Ok)await TodoEndpointsV1.GetTodo(1, context); - var notFoundResult = (NotFound)await TodoEndpointsV1.GetTodo(404, context); //Assert Assert.Equal(200, okResult.StatusCode); var foundTodo = Assert.IsAssignableFrom(okResult.Value); Assert.Equal(1, foundTodo.Id); - - Assert.Equal(404, notFoundResult.StatusCode); } [Fact] @@ -89,15 +99,12 @@ public async Task UpdateTodoUpdatesTodoInDatabase() //Act var createdResult = (Created)await TodoEndpointsV1.UpdateTodo(updatedTodo, context); - var notFoundResult = (NotFound)await TodoEndpointsV1.UpdateTodo(new Todo { Id = 2, Title = "Invalid Title" }, context); //Assert Assert.Equal(201, createdResult.StatusCode); Assert.NotNull(createdResult.Location); Assert.IsAssignableFrom(createdResult.Value); - Assert.Equal(404, notFoundResult.StatusCode); - var todoInDb = await context.Todos.FindAsync(1); Assert.NotNull(todoInDb); @@ -109,27 +116,24 @@ public async Task UpdateTodoUpdatesTodoInDatabase() public async Task DeleteTodoDeletesTodoInDatabase() { //Arrange - var existingTodo = new Todo() + await using var context = new MockDb().CreateDbContext(); + + var existingTodo = new Todo { Id = 1, Title = "Exiting test title", IsDone = false }; - await using var context = new MockDb().CreateDbContext(); - context.Todos.Add(existingTodo); await context.SaveChangesAsync(); //Act var noContentResult = (NoContent)await TodoEndpointsV1.DeleteTodo(existingTodo.Id, context); - var notFoundResult = (NotFound)await TodoEndpointsV1.DeleteTodo(2, context); //Assert Assert.Equal(204, noContentResult.StatusCode); Assert.Empty(context.Todos); - - Assert.Equal(404, notFoundResult.StatusCode); } } diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoMoqTests.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoMoqTests.cs index acc11959b5e6..77837ad845ed 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoMoqTests.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoMoqTests.cs @@ -8,6 +8,22 @@ namespace MinApiTests.UnitTests; public class TodoMoqTests { + [Fact] + public async Task GetTodoReturnsNotFoundIfNotExists() + { + // Arrange + var mock = new Mock(); + + mock.Setup(m => m.Find(It.Is(id => id == 404))) + .ReturnsAsync((Todo?)null); + + // Act + var notFoundResult = (NotFound)await TodoEndpointsV2.GetTodo(404, mock.Object); + + //Assert + Assert.Equal(404, notFoundResult.StatusCode); + } + [Fact] public async Task GetTodoReturnsTodoFromDatabase() { @@ -22,19 +38,13 @@ public async Task GetTodoReturnsTodoFromDatabase() IsDone = false }); - mock.Setup(m => m.Find(It.Is(id => id == 2))) - .ReturnsAsync((Todo?)null); - // Act var okResult = (Ok)await TodoEndpointsV2.GetTodo(1, mock.Object); - var notFoundResult = (NotFound)await TodoEndpointsV2.GetTodo(404, mock.Object); //Assert Assert.Equal(200, okResult.StatusCode); var foundTodo = Assert.IsAssignableFrom(okResult.Value); Assert.Equal(1, foundTodo.Id); - - Assert.Equal(404, notFoundResult.StatusCode); } [Fact] @@ -96,16 +106,12 @@ public async Task UpdateTodoUpdatesTodoInDatabase() mock.Setup(m => m.Find(It.Is(id => id == 1))) .ReturnsAsync(existingTodo); - mock.Setup(m => m.Find(It.Is(id => id == 2))) - .ReturnsAsync((Todo?)null); - mock.Setup(m => m.Update(It.Is(t => t.Id == updatedTodo.Id && t.Description == updatedTodo.Description && t.IsDone == updatedTodo.IsDone))) - .Callback((todo) => existingTodo = todo) + .Callback(todo => existingTodo = todo) .Returns(Task.CompletedTask); //Act var createdResult = (Created)await TodoEndpointsV2.UpdateTodo(updatedTodo, mock.Object); - var notFoundResult = (NotFound)await TodoEndpointsV2.UpdateTodo(new Todo { Id = 2, Title = "Invalid Title" }, mock.Object); //Assert Assert.Equal(201, createdResult.StatusCode); @@ -114,8 +120,6 @@ public async Task UpdateTodoUpdatesTodoInDatabase() Assert.Equal("Updated test title", existingTodo.Title); Assert.True(existingTodo.IsDone); - - Assert.Equal(404, notFoundResult.StatusCode); } [Fact] @@ -136,21 +140,15 @@ public async Task DeleteTodoDeletesTodoInDatabase() mock.Setup(m => m.Find(It.Is(id => id == existingTodo.Id))) .ReturnsAsync(existingTodo); - mock.Setup(m => m.Find(It.Is(id => id == 2))) - .ReturnsAsync((Todo?)null); - mock.Setup(m => m.Remove(It.Is(t => t.Id == 1))) .Callback(t => todos.Remove(t)) .Returns(Task.CompletedTask); //Act var noContentResult = (NoContent)await TodoEndpointsV2.DeleteTodo(existingTodo.Id, mock.Object); - var notFoundResult = (NotFound)await TodoEndpointsV2.DeleteTodo(2, mock.Object); //Assert Assert.Equal(204, noContentResult.StatusCode); Assert.Empty(todos); - - Assert.Equal(404, notFoundResult.StatusCode); } } From d67e14d28b17fa1d816fdcb24aa9d9f7c98befde Mon Sep 17 00:00:00 2001 From: Fiyaz Bin Hasan Date: Sat, 17 Sep 2022 03:48:22 +0600 Subject: [PATCH 14/18] minor refactoring --- .../MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs | 2 +- .../MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs index 38ef6d939866..3dfef16a4bee 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs @@ -36,7 +36,7 @@ public async Task PostTodoWithValidationProblems(TodoDto todo, string errorMessa var problemResult = await response.Content.ReadFromJsonAsync(); Assert.NotNull(problemResult?.Errors); - Assert.Collection(problemResult?.Errors, (error) => Assert.Equal(errorMessage, error.Value.First())); + Assert.Collection(problemResult.Errors, (error) => Assert.Equal(errorMessage, error.Value.First())); } [Fact] diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs index d5b0ec9d7391..cbdf744286d5 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs @@ -38,7 +38,7 @@ public async Task PostTodoWithValidationProblems(TodoDto todo, string errorMessa var problemResult = await response.Content.ReadFromJsonAsync(); Assert.NotNull(problemResult?.Errors); - Assert.Collection(problemResult?.Errors, (error) => Assert.Equal(errorMessage, error.Value.First())); + Assert.Collection(problemResult.Errors, (error) => Assert.Equal(errorMessage, error.Value.First())); } [Fact] @@ -50,7 +50,7 @@ public async Task PostTodoWithValidParameters() if (db != null && db.Todos.Any()) { db.Todos.RemoveRange(db.Todos); - db.SaveChanges(); + await db.SaveChangesAsync(); } } From 57e9ddb4233b9da89627a0b71ddf338981699d1d Mon Sep 17 00:00:00 2001 From: Fiyaz Bin Hasan Date: Sat, 17 Sep 2022 03:48:51 +0600 Subject: [PATCH 15/18] Update Program.cs --- .../minimal-apis/7.0-samples/WebMinRouteGroup/Program.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Program.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Program.cs index dc1c2739b84d..9cb7fd7f3af1 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Program.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Program.cs @@ -29,7 +29,6 @@ if (app.Environment.IsDevelopment()) { - // localhost:{port}/swagger app.UseSwagger(); app.UseSwaggerUI(); } @@ -37,7 +36,7 @@ app.MapGet("/", () => "Hello World!"); app.MapGet("/admin", () => "Authorized Endpoint") - .RequireAuthorization("AdminsOnly"); ; + .RequireAuthorization("AdminsOnly"); // todoV1 endpoints app.MapGroup("/todos/v1") From f68a8156a6ae500bca5bc64de615bda34c35b9de Mon Sep 17 00:00:00 2001 From: Fiyaz Bin Hasan Date: Sat, 17 Sep 2022 12:14:41 +0600 Subject: [PATCH 16/18] move projects in a solution --- .../AuthorizedEndpointsIntegrationTests.cs | 0 .../Helpers/TestAuthHandler.cs | 0 .../Helpers/TestEmailService.cs | 0 .../Helpers/TestWebApplicationFactory.cs | 0 .../TodoIntegrationTestsV1.cs | 0 .../TodoIntegrationTestsV2.cs | 0 .../MinApiTests/MinApiTests.csproj | 0 .../MinApiTests/UnitTests/Helpers/MockDb.cs | 0 .../UnitTests/TodoInMemoryTests.cs | 0 .../MinApiTests/UnitTests/TodoMoqTests.cs | 0 .../MinApiTests/Usings.cs | 0 .../MinApiTestsSample/MinApiTestsSample.sln | 31 +++++++++++++++++++ .../WebMinRouteGroup/Data/Todo.cs | 0 .../WebMinRouteGroup/Data/TodoDto.cs | 0 .../Data/TodoGroupDbContext.cs | 0 .../20220821140431_InitialCreate.Designer.cs | 0 .../20220821140431_InitialCreate.cs | 0 .../TodoGroupDbContextModelSnapshot.cs | 0 .../WebMinRouteGroup/Program.cs | 0 .../WebMinRouteGroup/Services/EmailService.cs | 0 .../Services/IEmailService.cs | 0 .../WebMinRouteGroup/Services/ITodoService.cs | 0 .../WebMinRouteGroup/Services/TodoService.cs | 0 .../WebMinRouteGroup/TodoEndpointsV1.cs | 0 .../WebMinRouteGroup/TodoEndpointsV2.cs | 0 .../WebMinRouteGroup/Utilities.cs | 2 +- .../WebMinRouteGroup/WebMinRouteGroup.csproj | 0 .../appsettings.Development.json | 0 .../WebMinRouteGroup/appsettings.json | 0 29 files changed, 32 insertions(+), 1 deletion(-) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/MinApiTests/IntegrationTests/AuthorizedEndpointsIntegrationTests.cs (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/MinApiTests/IntegrationTests/Helpers/TestAuthHandler.cs (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/MinApiTests/IntegrationTests/Helpers/TestEmailService.cs (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/MinApiTests/IntegrationTests/Helpers/TestWebApplicationFactory.cs (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/MinApiTests/MinApiTests.csproj (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/MinApiTests/UnitTests/Helpers/MockDb.cs (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/MinApiTests/UnitTests/TodoInMemoryTests.cs (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/MinApiTests/UnitTests/TodoMoqTests.cs (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/MinApiTests/Usings.cs (100%) create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTestsSample.sln rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/WebMinRouteGroup/Data/Todo.cs (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/WebMinRouteGroup/Data/TodoDto.cs (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/WebMinRouteGroup/Data/TodoGroupDbContext.cs (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/WebMinRouteGroup/Migrations/20220821140431_InitialCreate.Designer.cs (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/WebMinRouteGroup/Migrations/20220821140431_InitialCreate.cs (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/WebMinRouteGroup/Migrations/TodoGroupDbContextModelSnapshot.cs (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/WebMinRouteGroup/Program.cs (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/WebMinRouteGroup/Services/EmailService.cs (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/WebMinRouteGroup/Services/IEmailService.cs (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/WebMinRouteGroup/Services/ITodoService.cs (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/WebMinRouteGroup/Services/TodoService.cs (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/WebMinRouteGroup/TodoEndpointsV1.cs (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/WebMinRouteGroup/TodoEndpointsV2.cs (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/WebMinRouteGroup/Utilities.cs (93%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/WebMinRouteGroup/WebMinRouteGroup.csproj (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/WebMinRouteGroup/appsettings.Development.json (100%) rename aspnetcore/fundamentals/minimal-apis/7.0-samples/{ => MinApiTestsSample}/WebMinRouteGroup/appsettings.json (100%) diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/AuthorizedEndpointsIntegrationTests.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/IntegrationTests/AuthorizedEndpointsIntegrationTests.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/AuthorizedEndpointsIntegrationTests.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/IntegrationTests/AuthorizedEndpointsIntegrationTests.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestAuthHandler.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/IntegrationTests/Helpers/TestAuthHandler.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestAuthHandler.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/IntegrationTests/Helpers/TestAuthHandler.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestEmailService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/IntegrationTests/Helpers/TestEmailService.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestEmailService.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/IntegrationTests/Helpers/TestEmailService.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestWebApplicationFactory.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/IntegrationTests/Helpers/TestWebApplicationFactory.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/Helpers/TestWebApplicationFactory.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/IntegrationTests/Helpers/TestWebApplicationFactory.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/IntegrationTests/TodoIntegrationTestsV1.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/IntegrationTests/TodoIntegrationTestsV2.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/MinApiTests.csproj similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/MinApiTests.csproj rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/MinApiTests.csproj diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/Helpers/MockDb.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/UnitTests/Helpers/MockDb.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/Helpers/MockDb.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/UnitTests/Helpers/MockDb.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoInMemoryTests.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/UnitTests/TodoInMemoryTests.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoInMemoryTests.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/UnitTests/TodoInMemoryTests.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoMoqTests.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/UnitTests/TodoMoqTests.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/UnitTests/TodoMoqTests.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/UnitTests/TodoMoqTests.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/Usings.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/Usings.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTests/Usings.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/Usings.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTestsSample.sln b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTestsSample.sln new file mode 100644 index 000000000000..e59a954f3fa5 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTestsSample.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebMinRouteGroup", "WebMinRouteGroup\WebMinRouteGroup.csproj", "{3256AE2D-3E23-4869-B914-51A04C2357CF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MinApiTests", "MinApiTests\MinApiTests.csproj", "{2B14F621-3DC6-40D5-87F5-469CDE54F788}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3256AE2D-3E23-4869-B914-51A04C2357CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3256AE2D-3E23-4869-B914-51A04C2357CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3256AE2D-3E23-4869-B914-51A04C2357CF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3256AE2D-3E23-4869-B914-51A04C2357CF}.Release|Any CPU.Build.0 = Release|Any CPU + {2B14F621-3DC6-40D5-87F5-469CDE54F788}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2B14F621-3DC6-40D5-87F5-469CDE54F788}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2B14F621-3DC6-40D5-87F5-469CDE54F788}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2B14F621-3DC6-40D5-87F5-469CDE54F788}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {47B1B0C6-1AC8-4B66-84E5-6F4A09F2E1BB} + EndGlobalSection +EndGlobal diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Data/Todo.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Data/Todo.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Data/Todo.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Data/Todo.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Data/TodoDto.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Data/TodoDto.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Data/TodoDto.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Data/TodoDto.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Data/TodoGroupDbContext.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Data/TodoGroupDbContext.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Data/TodoGroupDbContext.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Data/TodoGroupDbContext.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Migrations/20220821140431_InitialCreate.Designer.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Migrations/20220821140431_InitialCreate.Designer.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Migrations/20220821140431_InitialCreate.Designer.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Migrations/20220821140431_InitialCreate.Designer.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Migrations/20220821140431_InitialCreate.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Migrations/20220821140431_InitialCreate.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Migrations/20220821140431_InitialCreate.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Migrations/20220821140431_InitialCreate.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Migrations/TodoGroupDbContextModelSnapshot.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Migrations/TodoGroupDbContextModelSnapshot.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Migrations/TodoGroupDbContextModelSnapshot.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Migrations/TodoGroupDbContextModelSnapshot.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Program.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Program.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Program.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Program.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/EmailService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Services/EmailService.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/EmailService.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Services/EmailService.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/IEmailService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Services/IEmailService.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/IEmailService.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Services/IEmailService.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/ITodoService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Services/ITodoService.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/ITodoService.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Services/ITodoService.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/TodoService.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Services/TodoService.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Services/TodoService.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Services/TodoService.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/TodoEndpointsV1.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/TodoEndpointsV1.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/TodoEndpointsV1.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/TodoEndpointsV1.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/TodoEndpointsV2.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/TodoEndpointsV2.cs similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/TodoEndpointsV2.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/TodoEndpointsV2.cs diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Utilities.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Utilities.cs similarity index 93% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Utilities.cs rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Utilities.cs index 6544f6bb7f91..b1705011f700 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/Utilities.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/Utilities.cs @@ -13,7 +13,7 @@ public static Dictionary IsValid(TodoDto td) errors.TryAdd("todo.name.errors", new[] { "Name is empty" }); } - if (td.Title!.Length < 3) + if (td.Title.Length < 3) { errors.TryAdd("todo.name.errors", new[] { "Name length < 3" }); } diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/WebMinRouteGroup.csproj b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/WebMinRouteGroup.csproj similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/WebMinRouteGroup.csproj rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/WebMinRouteGroup.csproj diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/appsettings.Development.json b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/appsettings.Development.json similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/appsettings.Development.json rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/appsettings.Development.json diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/appsettings.json b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/appsettings.json similarity index 100% rename from aspnetcore/fundamentals/minimal-apis/7.0-samples/WebMinRouteGroup/appsettings.json rename to aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/WebMinRouteGroup/appsettings.json From 073d72f7da2fb8f75a345aed25c6e8220cdc05c8 Mon Sep 17 00:00:00 2001 From: Fiyaz Bin Hasan Date: Sat, 17 Sep 2022 13:29:05 +0600 Subject: [PATCH 17/18] bring back todo-group sample --- .../7.0-samples/todo-group/Data/Todo.cs | 8 +++ .../7.0-samples/todo-group/Data/TodoDto.cs | 8 +++ .../todo-group/Data/TodoGroupDbContext.cs | 14 ++++ .../7.0-samples/todo-group/Program.cs | 55 ++++++++++++++++ .../7.0-samples/todo-group/RouteHandlers.cs | 66 +++++++++++++++++++ .../todo-group/appsettings.Development.json | 8 +++ .../7.0-samples/todo-group/appsettings.json | 9 +++ .../7.0-samples/todo-group/todo-group.csproj | 16 +++++ 8 files changed, 184 insertions(+) create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/Todo.cs create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoDto.cs create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoGroupDbContext.cs create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/RouteHandlers.cs create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/appsettings.Development.json create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/appsettings.json create mode 100644 aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/todo-group.csproj diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/Todo.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/Todo.cs new file mode 100644 index 000000000000..1d231d41b812 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/Todo.cs @@ -0,0 +1,8 @@ +namespace Data; +public class Todo +{ + public int Id { get; set; } + public string Title { get; set; } = String.Empty; + public string Description { get; set; } = String.Empty; + public bool IsDone { get; set; } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoDto.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoDto.cs new file mode 100644 index 000000000000..a75996caaa43 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoDto.cs @@ -0,0 +1,8 @@ +namespace Data; + +public class TodoDto +{ + public string Title { get; set; } = String.Empty; + public string Description { get; set; } = String.Empty; + public bool IsDone { get; set; } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoGroupDbContext.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoGroupDbContext.cs new file mode 100644 index 000000000000..d17e99d46b59 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Data/TodoGroupDbContext.cs @@ -0,0 +1,14 @@ +using Microsoft.EntityFrameworkCore; + +namespace Data; + +public class TodoGroupDbContext : DbContext +{ + public TodoGroupDbContext(DbContextOptions options) + : base(options) + { + Database.EnsureCreated(); + } + + public DbSet Todos { get; set; } = default!; +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs new file mode 100644 index 000000000000..adc1fbfb8d87 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/Program.cs @@ -0,0 +1,55 @@ +using Data; +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; + +var builder = WebApplication.CreateBuilder(args); +var connection = new SqliteConnection("DataSource=:memory:"); +connection.Open(); + +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); +builder.Services.AddDbContext(options => +{ + options.UseSqlite(connection); +}); + +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) +{ + // localhost:{port}/swagger + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.MapGet("/", () => "Hello World!"); + +// todo endpoints +var todos = app.MapGroup("/todos").WithTags("Todo Endpoints").AddRouteHandlerFilter(async (context, next) => +{ + app.Logger.LogInformation("Accessing todo endpoints"); + return await next(context); +}); +todos.MapGet("/", RouteHandlers.GetAllTodos); +todos.MapGet("/{id}", RouteHandlers.GetTodo); +todos.MapPost("/", RouteHandlers.CreateTodo).AddRouteHandlerFilter(async (context, next) => +{ + // log time taken to process + var start = DateTime.Now; + var result = await next(context); + var end = DateTime.Now; + app.Logger.LogInformation($"{context.HttpContext.Request.Path.Value} took {(end - start).TotalMilliseconds}ms"); + return result; +}); +todos.MapPut("/{id}", RouteHandlers.UpdateTodo).AddRouteHandlerFilter(async (context, next) => +{ + // log time taken to process + var start = DateTime.Now; + var result = await next(context); + var end = DateTime.Now; + app.Logger.LogInformation($"{context.HttpContext.Request.Path.Value} took {(end - start).TotalMilliseconds}ms"); + return result; +}); +todos.MapDelete("/{id}", RouteHandlers.DeleteTodo); + +app.Run(); diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/RouteHandlers.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/RouteHandlers.cs new file mode 100644 index 000000000000..cb2334ccfdb7 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/RouteHandlers.cs @@ -0,0 +1,66 @@ +using Data; +using Microsoft.EntityFrameworkCore; + +public class RouteHandlers +{ + // get all todos + public static async Task GetAllTodos(TodoGroupDbContext database) + { + var todos = await database.Todos.ToListAsync(); + return TypedResults.Ok(todos); + } + + // get todo by id + public static async Task GetTodo(int id, TodoGroupDbContext database) + { + var todo = await database.Todos.FindAsync(id); + if (todo != null) + { + return TypedResults.Ok(todo); + } + return TypedResults.NotFound(); + } + + // create todo + public static async Task CreateTodo(TodoDto todo, TodoGroupDbContext database) + { + var newTodo = new Todo + { + Title = todo.Title, + Description = todo.Description, + IsDone = todo.IsDone + }; + await database.Todos.AddAsync(newTodo); + await database.SaveChangesAsync(); + return TypedResults.Created($"/public/todos/{newTodo.Id}", newTodo); + } + + // update todo + public static async Task UpdateTodo(Todo todo, TodoGroupDbContext database) + { + var existingTodo = await database.Todos.FindAsync(todo.Id); + if (existingTodo != null) + { + existingTodo.Title = todo.Title; + existingTodo.Description = todo.Description; + existingTodo.IsDone = todo.IsDone; + await database.SaveChangesAsync(); + return TypedResults.Created($"/public/todos/{existingTodo.Id}", existingTodo); + } + + return TypedResults.NotFound(); + } + + // delete todo + public static async Task DeleteTodo(int id, TodoGroupDbContext database) + { + var todo = await database.Todos.FindAsync(id); + if (todo != null) + { + database.Todos.Remove(todo); + await database.SaveChangesAsync(); + return TypedResults.NoContent(); + } + return TypedResults.NotFound(); + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/appsettings.Development.json b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/appsettings.Development.json new file mode 100644 index 000000000000..0c208ae9181e --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/appsettings.json b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/appsettings.json new file mode 100644 index 000000000000..10f68b8c8b4f --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/todo-group.csproj b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/todo-group.csproj new file mode 100644 index 000000000000..80f875509fdf --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/todo-group/todo-group.csproj @@ -0,0 +1,16 @@ + + + + net7.0 + enable + enable + todo_group + + + + + + + + + From 2303e2e2a23aaa23dfc274afe8cddc5c5e77f1d6 Mon Sep 17 00:00:00 2001 From: Fiyaz Bin Hasan Date: Sun, 18 Sep 2022 01:52:04 +0600 Subject: [PATCH 18/18] additional unit tests --- .../UnitTests/TodoInMemoryTests.cs | 45 ++++++++++ .../MinApiTests/UnitTests/TodoMoqTests.cs | 90 ++++++++++++++++++- 2 files changed, 133 insertions(+), 2 deletions(-) diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/UnitTests/TodoInMemoryTests.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/UnitTests/TodoInMemoryTests.cs index 5403bcce7010..2710882a474d 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/UnitTests/TodoInMemoryTests.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/UnitTests/TodoInMemoryTests.cs @@ -20,6 +20,51 @@ public async Task GetTodoReturnsNotFoundIfNotExists() Assert.Equal(404, notFoundResult.StatusCode); } + [Fact] + public async Task GetAllReturnsTodosFromDatabase() + { + // Arrange + await using var context = new MockDb().CreateDbContext(); + + context.Todos.Add(new Todo + { + Id = 1, + Title = "Test title 1", + Description = "Test description 1", + IsDone = false + }); + + context.Todos.Add(new Todo + { + Id = 2, + Title = "Test title 2", + Description = "Test description 2", + IsDone = true + }); + + await context.SaveChangesAsync(); + + // Act + var okResult = (Ok>)await TodoEndpointsV1.GetAllTodos(context); + + //Assert + Assert.Equal(200, okResult.StatusCode); + var foundTodos = Assert.IsAssignableFrom>(okResult.Value); + + Assert.NotEmpty(foundTodos); + Assert.Collection(foundTodos, todo1 => + { + Assert.Equal("Test title 1", todo1.Title); + Assert.Equal("Test description 1", todo1.Description); + Assert.False(todo1.IsDone); + }, todo2 => + { + Assert.Equal("Test title 2", todo2.Title); + Assert.Equal("Test description 2", todo2.Description); + Assert.True(todo2.IsDone); + }); + } + [Fact] public async Task GetTodoReturnsTodoFromDatabase() { diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/UnitTests/TodoMoqTests.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/UnitTests/TodoMoqTests.cs index 77837ad845ed..d3075b0afa55 100644 --- a/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/UnitTests/TodoMoqTests.cs +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/MinApiTestsSample/MinApiTests/UnitTests/TodoMoqTests.cs @@ -12,8 +12,8 @@ public class TodoMoqTests public async Task GetTodoReturnsNotFoundIfNotExists() { // Arrange - var mock = new Mock(); - + var mock = new Mock(); + mock.Setup(m => m.Find(It.Is(id => id == 404))) .ReturnsAsync((Todo?)null); @@ -24,6 +24,92 @@ public async Task GetTodoReturnsNotFoundIfNotExists() Assert.Equal(404, notFoundResult.StatusCode); } + [Fact] + public async Task GetAllReturnsTodosFromDatabase() + { + // Arrange + var mock = new Mock(); + + mock.Setup(m => m.GetAll()) + .ReturnsAsync(new List { + new Todo + { + Id = 1, + Title = "Test title 1", + IsDone = false + }, + new Todo + { + Id = 2, + Title = "Test title 2", + IsDone = true + } + }); + + // Act + var okResult = (Ok>)await TodoEndpointsV2.GetAllTodos(mock.Object); + + //Assert + Assert.Equal(200, okResult.StatusCode); + var foundTodos = Assert.IsAssignableFrom>(okResult.Value); + + Assert.NotEmpty(foundTodos); + Assert.Collection(foundTodos, todo1 => + { + Assert.Equal(1, todo1.Id); + Assert.Equal("Test title 1", todo1.Title); + Assert.False(todo1.IsDone); + }, todo2 => + { + Assert.Equal(2, todo2.Id); + Assert.Equal("Test title 2", todo2.Title); + Assert.True(todo2.IsDone); + }); + } + + [Fact] + public async Task GetAllIncompletedReturnsIncompletedTodosFromDatabase() + { + // Arrange + var mock = new Mock(); + + mock.Setup(m => m.GetIncompleteTodos()) + .ReturnsAsync(new List { + new Todo + { + Id = 1, + Title = "Test title 1", + IsDone = false + }, + new Todo + { + Id = 2, + Title = "Test title 2", + IsDone = false + } + }); + + // Act + var okResult = (Ok>)await TodoEndpointsV2.GetAllIncompletedTodos(mock.Object); + + //Assert + Assert.Equal(200, okResult.StatusCode); + var foundTodos = Assert.IsAssignableFrom>(okResult.Value); + + Assert.NotEmpty(foundTodos); + Assert.Collection(foundTodos, todo1 => + { + Assert.Equal(1, todo1.Id); + Assert.Equal("Test title 1", todo1.Title); + Assert.False(todo1.IsDone); + }, todo2 => + { + Assert.Equal(2, todo2.Id); + Assert.Equal("Test title 2", todo2.Title); + Assert.False(todo2.IsDone); + }); + } + [Fact] public async Task GetTodoReturnsTodoFromDatabase() {