diff --git a/BotwinMediator/BotwinMediator.csproj b/BotwinMediator/BotwinMediator.csproj
index ba38131..acc63fe 100644
--- a/BotwinMediator/BotwinMediator.csproj
+++ b/BotwinMediator/BotwinMediator.csproj
@@ -9,6 +9,7 @@
+
diff --git a/BotwinMediator/Features/Films/FilmsModule.cs b/BotwinMediator/Features/Films/FilmsModule.cs
index 4e7fdfa..ac2a85c 100644
--- a/BotwinMediator/Features/Films/FilmsModule.cs
+++ b/BotwinMediator/Features/Films/FilmsModule.cs
@@ -59,7 +59,7 @@ public FilmsModule(Handler handler) : base("/api/films")
{
var command = new CreateFilmCommand(result.Data);
this.handler.Handle(command);
- res.StatusCode = 204;
+ res.StatusCode = 201;
}
catch (Exception)
{
diff --git a/BotwinMediator/Features/Films/ListFilmById/ListFilmsByIdCommandHandler.cs b/BotwinMediator/Features/Films/ListFilmById/ListFilmsByIdCommandHandler.cs
index 6e99df5..e7ee6b3 100644
--- a/BotwinMediator/Features/Films/ListFilmById/ListFilmsByIdCommandHandler.cs
+++ b/BotwinMediator/Features/Films/ListFilmById/ListFilmsByIdCommandHandler.cs
@@ -27,6 +27,11 @@ protected override object Execute(ListFilmsByIdCommand command)
//Use shared query to get film
var film = this.listFilmByIdQuery.Execute(command.Id);
+
+ if (film == null)
+ {
+ return null;
+ }
var director = this.getDirectorByIdQuery.Execute(film.DirectorId);
film.Director = director;
diff --git a/BotwinMediator/Program.cs b/BotwinMediator/Program.cs
index 892c9bb..b386cd6 100644
--- a/BotwinMediator/Program.cs
+++ b/BotwinMediator/Program.cs
@@ -1,19 +1,16 @@
namespace BotwinMediator
{
- using System.IO;
+ using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
public class Program
{
public static void Main(string[] args)
{
- var host = new WebHostBuilder()
- .UseContentRoot(Directory.GetCurrentDirectory())
- .UseKestrel()
+ WebHost.CreateDefaultBuilder(args)
.UseStartup()
- .Build();
-
- host.Run();
+ .Build()
+ .Run();
}
}
}
diff --git a/BotwinMediator/Startup.cs b/BotwinMediator/Startup.cs
index d88b60c..6514268 100644
--- a/BotwinMediator/Startup.cs
+++ b/BotwinMediator/Startup.cs
@@ -33,7 +33,7 @@ public void ConfigureServices(IServiceCollection services)
new UpdateFilmCommandHandler(s.GetRequiredService(),s.GetRequiredService())
}));
- services.AddBotwin(Assembly.GetEntryAssembly(), typeof(FilmValidator).Assembly);
+ services.AddBotwin(Assembly.GetCallingAssembly(), typeof(FilmValidator).Assembly);
}
public void Configure(IApplicationBuilder app)
diff --git a/DDDScot.sln b/DDDScot.sln
index 51bc8e0..61bdf09 100644
--- a/DDDScot.sln
+++ b/DDDScot.sln
@@ -31,6 +31,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2. MediatR Project Tests",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediatRWebAPI.Tests", "MediatRWebAPI.Tests\MediatRWebAPI.Tests.csproj", "{E9EA372B-B08D-4C2E-B78A-948810D79F02}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3. Functional Project Tests", "3. Functional Project Tests", "{F03EA84D-15EF-4E1D-8FBE-79FFC0FDC277}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FunctionalProjectTests", "FunctionalProjectTests\FunctionalProjectTests.csproj", "{0A4CAD93-00B1-4ED6-9C92-3DB4C4F8509A}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -101,6 +105,18 @@ Global
{E9EA372B-B08D-4C2E-B78A-948810D79F02}.Release|x64.Build.0 = Release|Any CPU
{E9EA372B-B08D-4C2E-B78A-948810D79F02}.Release|x86.ActiveCfg = Release|Any CPU
{E9EA372B-B08D-4C2E-B78A-948810D79F02}.Release|x86.Build.0 = Release|Any CPU
+ {0A4CAD93-00B1-4ED6-9C92-3DB4C4F8509A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0A4CAD93-00B1-4ED6-9C92-3DB4C4F8509A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0A4CAD93-00B1-4ED6-9C92-3DB4C4F8509A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0A4CAD93-00B1-4ED6-9C92-3DB4C4F8509A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0A4CAD93-00B1-4ED6-9C92-3DB4C4F8509A}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {0A4CAD93-00B1-4ED6-9C92-3DB4C4F8509A}.Debug|x64.Build.0 = Debug|Any CPU
+ {0A4CAD93-00B1-4ED6-9C92-3DB4C4F8509A}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {0A4CAD93-00B1-4ED6-9C92-3DB4C4F8509A}.Debug|x86.Build.0 = Debug|Any CPU
+ {0A4CAD93-00B1-4ED6-9C92-3DB4C4F8509A}.Release|x64.ActiveCfg = Release|Any CPU
+ {0A4CAD93-00B1-4ED6-9C92-3DB4C4F8509A}.Release|x64.Build.0 = Release|Any CPU
+ {0A4CAD93-00B1-4ED6-9C92-3DB4C4F8509A}.Release|x86.ActiveCfg = Release|Any CPU
+ {0A4CAD93-00B1-4ED6-9C92-3DB4C4F8509A}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{626AD192-F5D0-4AE1-AEEC-7B8FE40E927F} = {66D3DCED-78BD-4DB8-8C8D-3A5FA09543C6}
@@ -115,5 +131,7 @@ Global
{2974A21D-76F8-4248-BF01-E7B5BDA6A6FF} = {DD7F5385-6594-4AF5-ADE9-ABC65C2A29BC}
{694570A7-D60F-4058-A86F-B146204891B7} = {44C1DD68-1371-456C-9541-BCC3CEE8A2BE}
{E9EA372B-B08D-4C2E-B78A-948810D79F02} = {694570A7-D60F-4058-A86F-B146204891B7}
+ {F03EA84D-15EF-4E1D-8FBE-79FFC0FDC277} = {44C1DD68-1371-456C-9541-BCC3CEE8A2BE}
+ {0A4CAD93-00B1-4ED6-9C92-3DB4C4F8509A} = {F03EA84D-15EF-4E1D-8FBE-79FFC0FDC277}
EndGlobalSection
EndGlobal
diff --git a/FunctionalProject/Features/Films/FilmsModule.cs b/FunctionalProject/Features/Films/FilmsModule.cs
deleted file mode 100644
index cbaa150..0000000
--- a/FunctionalProject/Features/Films/FilmsModule.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace FunctionalProject.Features.Films
-{
- using System.Threading.Tasks;
- using Botwin;
- using Botwin.Response;
- using Microsoft.AspNetCore.Http;
-
- public class FilmsModule : BotwinModule
- {
- public FilmsModule() : base("/api/films")
- {
- this.Get("/", GetFilms);
- }
-
- private async Task GetFilms(HttpContext context)
- {
-
- }
- }
-}
diff --git a/FunctionalProject/Features/Films/RouteHandlers.cs b/FunctionalProject/Features/Films/RouteHandlers.cs
deleted file mode 100644
index 7a6d379..0000000
--- a/FunctionalProject/Features/Films/RouteHandlers.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace FunctionalProject.Features.Films
-{
- public static class RouteHandlers
- {
-
- }
-}
diff --git a/FunctionalProject/Features/FuncFilms/CreateFilm/CreateFilmRoute.cs b/FunctionalProject/Features/FuncFilms/CreateFilm/CreateFilmRoute.cs
new file mode 100644
index 0000000..43e4146
--- /dev/null
+++ b/FunctionalProject/Features/FuncFilms/CreateFilm/CreateFilmRoute.cs
@@ -0,0 +1,20 @@
+namespace FunctionalProject.Features.FuncFilms.CreateFilm
+{
+ using System;
+ using Models;
+
+ public static class CreateFilmRoute
+ {
+ public static void Handle(Film film, Func validUserQuery)
+ {
+ if (!validUserQuery())
+ {
+ throw new InvalidOperationException();
+ }
+
+ //Do some special MEGA CORP business validation
+
+ //Save to database by writing SQL here
+ }
+ }
+}
diff --git a/FunctionalProject/Features/FuncFilms/DeleteFilm/DeleteFilmRoute.cs b/FunctionalProject/Features/FuncFilms/DeleteFilm/DeleteFilmRoute.cs
new file mode 100644
index 0000000..a942bd4
--- /dev/null
+++ b/FunctionalProject/Features/FuncFilms/DeleteFilm/DeleteFilmRoute.cs
@@ -0,0 +1,17 @@
+namespace FunctionalProject.Features.FuncFilms.DeleteFilm
+{
+ using System;
+
+ public static class DeleteFilmRoute
+ {
+ public static void Handle(int id, Func validUserQuery)
+ {
+ if (!validUserQuery())
+ {
+ throw new InvalidOperationException();
+ }
+
+ //Write some SQL to delete from DB
+ }
+ }
+}
diff --git a/FunctionalProject/Features/DelegateFilms/FilmsModule.cs b/FunctionalProject/Features/FuncFilms/FilmsModule.cs
similarity index 57%
rename from FunctionalProject/Features/DelegateFilms/FilmsModule.cs
rename to FunctionalProject/Features/FuncFilms/FilmsModule.cs
index 4591b2b..261b5b6 100644
--- a/FunctionalProject/Features/DelegateFilms/FilmsModule.cs
+++ b/FunctionalProject/Features/FuncFilms/FilmsModule.cs
@@ -1,4 +1,4 @@
-namespace FunctionalProject.Features.DelegateFilms
+namespace FunctionalProject.Features.FuncFilms
{
using System;
using System.Threading.Tasks;
@@ -12,11 +12,38 @@
public class FilmsModule : BotwinModule
{
- public FilmsModule() : base("/api/films")
+ public FilmsModule() : base("/api/funcy/films")
{
this.Get("/", this.GetFilms);
this.Get("/{id:int}", this.GetFilmById);
this.Put("/{id:int}", this.UpdateFilm);
+ this.Post("/", this.CreateFilm);
+ this.Delete("/{id:int}", this.DeleteFilm);
+ }
+
+ private async Task CreateFilm(HttpContext context)
+ {
+ var result = context.Request.BindAndValidate();
+
+ if (!result.ValidationResult.IsValid)
+ {
+ context.Response.StatusCode = 422;
+ await context.Response.Negotiate(result.ValidationResult.GetFormattedErrors());
+ return;
+ }
+
+ try
+ {
+ var handler = RouteHandlers.CreateFilmHandler;
+
+ handler(result.Data);
+
+ context.Response.StatusCode = 201;
+ }
+ catch (Exception)
+ {
+ context.Response.StatusCode = 403;
+ }
}
private async Task UpdateFilm(HttpContext context)
@@ -35,7 +62,7 @@ private async Task UpdateFilm(HttpContext context)
var handler = RouteHandlers.UpdateFilmHandler;
handler(context.GetRouteData().As("id"), result.Data);
-
+
context.Response.StatusCode = 204;
}
catch (Exception)
@@ -67,5 +94,23 @@ private async Task GetFilmById(HttpContext context)
await context.Response.AsJson(film);
}
+
+ private Task DeleteFilm(HttpContext context)
+ {
+ try
+ {
+ var handler = RouteHandlers.DeleteFilmHandler;
+
+ handler(context.GetRouteData().As("id"));
+
+ context.Response.StatusCode = 204;
+ }
+ catch (Exception)
+ {
+ context.Response.StatusCode = 403;
+ }
+
+ return Task.CompletedTask;
+ }
}
}
diff --git a/FunctionalProject/Features/DelegateFilms/ListFilmById/ListFilmByIdRoute.cs b/FunctionalProject/Features/FuncFilms/ListFilmById/ListFilmByIdRoute.cs
similarity index 77%
rename from FunctionalProject/Features/DelegateFilms/ListFilmById/ListFilmByIdRoute.cs
rename to FunctionalProject/Features/FuncFilms/ListFilmById/ListFilmByIdRoute.cs
index 5a660d7..2b30938 100644
--- a/FunctionalProject/Features/DelegateFilms/ListFilmById/ListFilmByIdRoute.cs
+++ b/FunctionalProject/Features/FuncFilms/ListFilmById/ListFilmByIdRoute.cs
@@ -1,4 +1,4 @@
-namespace FunctionalProject.Features.DelegateFilms.ListFilmById
+namespace FunctionalProject.Features.FuncFilms.ListFilmById
{
using System;
using System.Collections.Generic;
@@ -9,6 +9,11 @@ public static class ListFilmByIdRoute
public static Film Handle(int id, Func listFilmById, Func getDirectorById, Func> getCastByFilmIdQuery)
{
var film = listFilmById(id);
+
+ if (film == null)
+ {
+ return null;
+ }
var director = getDirectorById(film.DirectorId);
film.Director = director;
diff --git a/FunctionalProject/Features/DelegateFilms/ListFilms/ListFilmsRoute.cs b/FunctionalProject/Features/FuncFilms/ListFilms/ListFilmsRoute.cs
similarity index 82%
rename from FunctionalProject/Features/DelegateFilms/ListFilms/ListFilmsRoute.cs
rename to FunctionalProject/Features/FuncFilms/ListFilms/ListFilmsRoute.cs
index 7e0197e..2783be7 100644
--- a/FunctionalProject/Features/DelegateFilms/ListFilms/ListFilmsRoute.cs
+++ b/FunctionalProject/Features/FuncFilms/ListFilms/ListFilmsRoute.cs
@@ -1,4 +1,4 @@
-namespace FunctionalProject.Features.DelegateFilms.ListFilms
+namespace FunctionalProject.Features.FuncFilms.ListFilms
{
using System.Collections.Generic;
using Models;
diff --git a/FunctionalProject/Features/DelegateFilms/RouteHandlers.cs b/FunctionalProject/Features/FuncFilms/RouteHandlers.cs
similarity index 66%
rename from FunctionalProject/Features/DelegateFilms/RouteHandlers.cs
rename to FunctionalProject/Features/FuncFilms/RouteHandlers.cs
index da3444e..72af230 100644
--- a/FunctionalProject/Features/DelegateFilms/RouteHandlers.cs
+++ b/FunctionalProject/Features/FuncFilms/RouteHandlers.cs
@@ -1,10 +1,12 @@
-namespace FunctionalProject.Features.DelegateFilms
+namespace FunctionalProject.Features.FuncFilms
{
using System;
using System.Collections.Generic;
- using FunctionalProject.Features.DelegateFilms.ListFilmById;
- using FunctionalProject.Features.DelegateFilms.ListFilms;
- using FunctionalProject.Features.DelegateFilms.UpdateFilm;
+ using FunctionalProject.Features.FuncFilms.CreateFilm;
+ using FunctionalProject.Features.FuncFilms.DeleteFilm;
+ using FunctionalProject.Features.FuncFilms.ListFilmById;
+ using FunctionalProject.Features.FuncFilms.ListFilms;
+ using FunctionalProject.Features.FuncFilms.UpdateFilm;
using Models;
public static class RouteHandlers
@@ -15,6 +17,10 @@ public static class RouteHandlers
private static Action updateFilm;
+ private static Action createFilm;
+
+ private static Action deleteFilm;
+
//private static Func getFilmyById = i => new Film { Id = 1, Name = "Pulp Fiction", DirectorId = 1 };
public static Func> ListFilmsHandler
@@ -51,5 +57,17 @@ public static Action UpdateFilmHandler
set => updateFilm = value;
}
+
+ public static Action CreateFilmHandler
+ {
+ get => createFilm ?? (film => CreateFilmRoute.Handle(film, () => new Random().Next() % 2 == 0));
+ set => createFilm = value;
+ }
+
+ public static Action DeleteFilmHandler
+ {
+ get => deleteFilm ?? (id => DeleteFilmRoute.Handle(id, () => new Random().Next() % 2 == 0));
+ set => deleteFilm = value;
+ }
}
}
diff --git a/FunctionalProject/Features/DelegateFilms/UpdateFilm/UpdateFilmRoute.cs b/FunctionalProject/Features/FuncFilms/UpdateFilm/UpdateFilmRoute.cs
similarity index 91%
rename from FunctionalProject/Features/DelegateFilms/UpdateFilm/UpdateFilmRoute.cs
rename to FunctionalProject/Features/FuncFilms/UpdateFilm/UpdateFilmRoute.cs
index 9ed63e3..2fb5b8b 100644
--- a/FunctionalProject/Features/DelegateFilms/UpdateFilm/UpdateFilmRoute.cs
+++ b/FunctionalProject/Features/FuncFilms/UpdateFilm/UpdateFilmRoute.cs
@@ -1,4 +1,4 @@
-namespace FunctionalProject.Features.DelegateFilms.UpdateFilm
+namespace FunctionalProject.Features.FuncFilms.UpdateFilm
{
using System;
using Models;
diff --git a/FunctionalProject/Features/NamedDelegatesFilms/CastMembers/GetCastByFilmIdQuery.cs b/FunctionalProject/Features/NamedDelegatesFilms/CastMembers/GetCastByFilmIdQuery.cs
new file mode 100644
index 0000000..2a5a1bd
--- /dev/null
+++ b/FunctionalProject/Features/NamedDelegatesFilms/CastMembers/GetCastByFilmIdQuery.cs
@@ -0,0 +1,15 @@
+namespace FunctionalProject.Features.NamedDelegatesFilms.CastMembers
+{
+ using System.Collections.Generic;
+ using Models;
+
+ public static class GetCastByFilmIdQuery
+ {
+ public static IEnumerable Execute(int filmId)
+ {
+ //Do some SQL
+
+ return new[] { new CastMember { Name = "John Travolta" }, new CastMember { Name = "Samuel L Jackson" } };
+ }
+ }
+}
diff --git a/FunctionalProject/Features/NamedDelegatesFilms/Directors/GetDirectorByIdQuery.cs b/FunctionalProject/Features/NamedDelegatesFilms/Directors/GetDirectorByIdQuery.cs
new file mode 100644
index 0000000..6e44a88
--- /dev/null
+++ b/FunctionalProject/Features/NamedDelegatesFilms/Directors/GetDirectorByIdQuery.cs
@@ -0,0 +1,14 @@
+namespace FunctionalProject.Features.NamedDelegatesFilms.Directors
+{
+ using Models;
+
+ public static class GetDirectorByIdQuery
+ {
+ public static Director Execute(int id)
+ {
+ //Do some SQL
+
+ return new Director { Name = "Steven Spielberg" };
+ }
+ }
+}
diff --git a/FunctionalProject/Features/NamedDelegatesFilms/Films/CreateFilm/CreateFilmRoute.cs b/FunctionalProject/Features/NamedDelegatesFilms/Films/CreateFilm/CreateFilmRoute.cs
new file mode 100644
index 0000000..3a7d1aa
--- /dev/null
+++ b/FunctionalProject/Features/NamedDelegatesFilms/Films/CreateFilm/CreateFilmRoute.cs
@@ -0,0 +1,20 @@
+namespace FunctionalProject.Features.NamedDelegatesFilms.Films.CreateFilm
+{
+ using System;
+ using Models;
+
+ public static class CreateFilmRoute
+ {
+ public static void Handle(Film film, ValidUserDelegate validUserQuery)
+ {
+ if (!validUserQuery())
+ {
+ throw new InvalidOperationException();
+ }
+
+ //Do some special MEGA CORP business validation
+
+ //Save to database by writing SQL here
+ }
+ }
+}
diff --git a/FunctionalProject/Features/NamedDelegatesFilms/Films/Delegates.cs b/FunctionalProject/Features/NamedDelegatesFilms/Films/Delegates.cs
new file mode 100644
index 0000000..a05566f
--- /dev/null
+++ b/FunctionalProject/Features/NamedDelegatesFilms/Films/Delegates.cs
@@ -0,0 +1,21 @@
+namespace FunctionalProject.Features.NamedDelegatesFilms.Films
+{
+ using System.Collections.Generic;
+ using Models;
+
+ public delegate Film ListFilmByIdDelegate(int id);
+
+ public delegate void CreateFilmDelegate(Film film);
+
+ public delegate void DeleteFilmDelegate(int id);
+
+ public delegate IEnumerable ListFilmsDelegate();
+
+ public delegate void UpdateFilmDelegate(int id, Film film);
+
+ public delegate bool ValidUserDelegate();
+
+ public delegate Director GetDirectorByIdDelegate(int id);
+
+ public delegate IEnumerable GetCastByFilmIdDelegate(int filmId);
+}
diff --git a/FunctionalProject/Features/NamedDelegatesFilms/Films/DeleteFilm/DeleteFilmRoute.cs b/FunctionalProject/Features/NamedDelegatesFilms/Films/DeleteFilm/DeleteFilmRoute.cs
new file mode 100644
index 0000000..d5ab6f3
--- /dev/null
+++ b/FunctionalProject/Features/NamedDelegatesFilms/Films/DeleteFilm/DeleteFilmRoute.cs
@@ -0,0 +1,17 @@
+namespace FunctionalProject.Features.NamedDelegatesFilms.Films.DeleteFilm
+{
+ using System;
+
+ public static class DeleteFilmRoute
+ {
+ public static void Handle(int id, ValidUserDelegate validUserQuery)
+ {
+ if (!validUserQuery())
+ {
+ throw new InvalidOperationException();
+ }
+
+ //Write some SQL to delete from DB
+ }
+ }
+}
diff --git a/FunctionalProject/Features/NamedDelegatesFilms/Films/FilmsModule.cs b/FunctionalProject/Features/NamedDelegatesFilms/Films/FilmsModule.cs
new file mode 100644
index 0000000..508f666
--- /dev/null
+++ b/FunctionalProject/Features/NamedDelegatesFilms/Films/FilmsModule.cs
@@ -0,0 +1,116 @@
+namespace FunctionalProject.Features.NamedDelegatesFilms.Films
+{
+ using System;
+ using System.Threading.Tasks;
+ using Botwin;
+ using Botwin.ModelBinding;
+ using Botwin.Request;
+ using Botwin.Response;
+ using Microsoft.AspNetCore.Http;
+ using Microsoft.AspNetCore.Routing;
+ using Models;
+
+ public class FilmsModule : BotwinModule
+ {
+ public FilmsModule() : base("/api/delegate/films")
+ {
+ this.Get("/", this.GetFilms);
+ this.Get("/{id:int}", this.GetFilmById);
+ this.Put("/{id:int}", this.UpdateFilm);
+ this.Post("/", this.CreateFilm);
+ this.Delete("/{id:int}", this.DeleteFilm);
+ }
+
+ private async Task CreateFilm(HttpContext context)
+ {
+ var result = context.Request.BindAndValidate();
+
+ if (!result.ValidationResult.IsValid)
+ {
+ context.Response.StatusCode = 422;
+ await context.Response.Negotiate(result.ValidationResult.GetFormattedErrors());
+ return;
+ }
+
+ try
+ {
+ var handler = RouteHandlers.CreateFilmHandler;
+
+ handler(result.Data);
+
+ context.Response.StatusCode = 201;
+ }
+ catch (Exception)
+ {
+ context.Response.StatusCode = 403;
+ }
+ }
+
+ private async Task UpdateFilm(HttpContext context)
+ {
+ var result = context.Request.BindAndValidate();
+
+ if (!result.ValidationResult.IsValid)
+ {
+ context.Response.StatusCode = 422;
+ await context.Response.Negotiate(result.ValidationResult.GetFormattedErrors());
+ return;
+ }
+
+ try
+ {
+ var handler = RouteHandlers.UpdateFilmHandler;
+
+ handler(context.GetRouteData().As("id"), result.Data);
+
+ context.Response.StatusCode = 204;
+ }
+ catch (Exception)
+ {
+ context.Response.StatusCode = 403;
+ }
+ }
+
+ private async Task GetFilms(HttpContext context)
+ {
+ var handler = RouteHandlers.ListFilmsHandler;
+
+ var films = handler();
+
+ await context.Response.AsJson(films);
+ }
+
+ private async Task GetFilmById(HttpContext context)
+ {
+ var handler = RouteHandlers.ListFilmByIdHandler;
+
+ var film = handler(context.GetRouteData().As("id"));
+
+ if (film == null)
+ {
+ context.Response.StatusCode = 404;
+ return;
+ }
+
+ await context.Response.AsJson(film);
+ }
+
+ private Task DeleteFilm(HttpContext context)
+ {
+ try
+ {
+ var handler = RouteHandlers.DeleteFilmHandler;
+
+ handler(context.GetRouteData().As("id"));
+
+ context.Response.StatusCode = 204;
+ }
+ catch (Exception)
+ {
+ context.Response.StatusCode = 403;
+ }
+
+ return Task.CompletedTask;
+ }
+ }
+}
diff --git a/FunctionalProject/Features/NamedDelegatesFilms/Films/ListFilmById/ListFilmByIdRoute.cs b/FunctionalProject/Features/NamedDelegatesFilms/Films/ListFilmById/ListFilmByIdRoute.cs
new file mode 100644
index 0000000..5d2f35d
--- /dev/null
+++ b/FunctionalProject/Features/NamedDelegatesFilms/Films/ListFilmById/ListFilmByIdRoute.cs
@@ -0,0 +1,25 @@
+namespace FunctionalProject.Features.NamedDelegatesFilms.Films.ListFilmById
+{
+ using Models;
+
+ public static class ListFilmByIdRoute
+ {
+ public static Film Handle(int id, ListFilmByIdDelegate listFilmById, GetDirectorByIdDelegate getDirectorByIdDelegate, GetCastByFilmIdDelegate getCastByFilmIdDelegateQuery)
+ {
+ var film = listFilmById(id);
+
+ if (film == null)
+ {
+ return null;
+ }
+
+ var director = getDirectorByIdDelegate(film.DirectorId);
+ film.Director = director;
+
+ var cast = getCastByFilmIdDelegateQuery(id);
+ film.Cast = cast;
+
+ return film;
+ }
+ }
+}
diff --git a/FunctionalProject/Features/NamedDelegatesFilms/Films/ListFilms/ListFilmsRoute.cs b/FunctionalProject/Features/NamedDelegatesFilms/Films/ListFilms/ListFilmsRoute.cs
new file mode 100644
index 0000000..2cb1b9d
--- /dev/null
+++ b/FunctionalProject/Features/NamedDelegatesFilms/Films/ListFilms/ListFilmsRoute.cs
@@ -0,0 +1,13 @@
+namespace FunctionalProject.Features.NamedDelegatesFilms.Films.ListFilms
+{
+ using System.Collections.Generic;
+ using Models;
+
+ public static class ListFilmsRoute
+ {
+ public static IEnumerable Handle()
+ {
+ return new[] { new Film { Id = 1, Name = "Pulp Fiction" }, new Film { Id = 2, Name = "Trainspotting" } };
+ }
+ }
+}
diff --git a/FunctionalProject/Features/NamedDelegatesFilms/Films/ListFilmsByIdQuery/ListFilmsByIdQuery.cs b/FunctionalProject/Features/NamedDelegatesFilms/Films/ListFilmsByIdQuery/ListFilmsByIdQuery.cs
new file mode 100644
index 0000000..3eec6a6
--- /dev/null
+++ b/FunctionalProject/Features/NamedDelegatesFilms/Films/ListFilmsByIdQuery/ListFilmsByIdQuery.cs
@@ -0,0 +1,12 @@
+namespace FunctionalProject.Features.NamedDelegatesFilms.Films.ListFilmsByIdQuery
+{
+ using Models;
+
+ public static class ListFilmsByIdQuery
+ {
+ public static Film Execute(int id)
+ {
+ return new Film { Id = 1, Name = "Pulp Fiction", DirectorId = 1 };
+ }
+ }
+}
diff --git a/FunctionalProject/Features/NamedDelegatesFilms/Films/Permissions/ValidUserQuery.cs b/FunctionalProject/Features/NamedDelegatesFilms/Films/Permissions/ValidUserQuery.cs
new file mode 100644
index 0000000..9ea76bc
--- /dev/null
+++ b/FunctionalProject/Features/NamedDelegatesFilms/Films/Permissions/ValidUserQuery.cs
@@ -0,0 +1,14 @@
+namespace FunctionalProject.Features.NamedDelegatesFilms.Films.Permissions
+{
+ using System;
+
+
+
+ public static class ValidUserQuery
+ {
+ public static bool Execute()
+ {
+ return new Random().Next() % 2 == 0;
+ }
+ }
+}
diff --git a/FunctionalProject/Features/NamedDelegatesFilms/Films/RouteHandlers.cs b/FunctionalProject/Features/NamedDelegatesFilms/Films/RouteHandlers.cs
new file mode 100644
index 0000000..41d30bc
--- /dev/null
+++ b/FunctionalProject/Features/NamedDelegatesFilms/Films/RouteHandlers.cs
@@ -0,0 +1,45 @@
+namespace FunctionalProject.Features.NamedDelegatesFilms.Films
+{
+ using FunctionalProject.Features.FuncFilms.UpdateFilm;
+ using FunctionalProject.Features.NamedDelegatesFilms.CastMembers;
+ using FunctionalProject.Features.NamedDelegatesFilms.Directors;
+ using FunctionalProject.Features.NamedDelegatesFilms.Films.CreateFilm;
+ using FunctionalProject.Features.NamedDelegatesFilms.Films.DeleteFilm;
+ using FunctionalProject.Features.NamedDelegatesFilms.Films.ListFilmById;
+ using FunctionalProject.Features.NamedDelegatesFilms.Films.ListFilms;
+ using FunctionalProject.Features.NamedDelegatesFilms.Films.Permissions;
+
+ public static class RouteHandlers
+ {
+ static RouteHandlers()
+ {
+ CreateFilmHandler = film => CreateFilmRoute.Handle(film, () => ValidUserQuery.Execute());
+
+ DeleteFilmHandler = id => DeleteFilmRoute.Handle(id, () => ValidUserQuery.Execute());
+
+ ListFilmByIdHandler = id => ListFilmByIdRoute.Handle(
+ id,
+ filmId => ListFilmsByIdQuery.ListFilmsByIdQuery.Execute(id),
+ dirId => GetDirectorByIdQuery.Execute(dirId),
+ filmId => GetCastByFilmIdQuery.Execute(id)
+ );
+
+ ListFilmsHandler = () => ListFilmsRoute.Handle();
+
+ UpdateFilmHandler = (id, film) => UpdateFilmRoute.Handle(
+ id,
+ film, () => ValidUserQuery.Execute(),
+ filmId => ListFilmsByIdQuery.ListFilmsByIdQuery.Execute(filmId));
+ }
+
+ public static CreateFilmDelegate CreateFilmHandler;
+
+ public static ListFilmByIdDelegate ListFilmByIdHandler;
+
+ public static DeleteFilmDelegate DeleteFilmHandler;
+
+ public static ListFilmsDelegate ListFilmsHandler;
+
+ public static UpdateFilmDelegate UpdateFilmHandler;
+ }
+}
diff --git a/FunctionalProject/Features/NamedDelegatesFilms/Films/UpdateFilm/UpdateFilmRoute.cs b/FunctionalProject/Features/NamedDelegatesFilms/Films/UpdateFilm/UpdateFilmRoute.cs
new file mode 100644
index 0000000..d7640a7
--- /dev/null
+++ b/FunctionalProject/Features/NamedDelegatesFilms/Films/UpdateFilm/UpdateFilmRoute.cs
@@ -0,0 +1,26 @@
+namespace FunctionalProject.Features.NamedDelegatesFilms.Films.UpdateFilm
+{
+ using System;
+ using Models;
+
+ public static class UpdateFilmRoute
+ {
+ public static void Handle(int id, Film film, ValidUserDelegate validUserQuery, ListFilmByIdDelegate listFilmById)
+ {
+ if (!validUserQuery())
+ {
+ throw new InvalidOperationException();
+ }
+
+ //Do some special MEGA CORP business validation
+
+ var existingFilm = listFilmById(id);
+
+ existingFilm.Name = film.Name;
+ existingFilm.Budget = film.Budget;
+ existingFilm.Language = film.Language;
+
+ //Write some SQL to store in db
+ }
+ }
+}
diff --git a/FunctionalProject/FunctionalProject.csproj b/FunctionalProject/FunctionalProject.csproj
index d1e6948..70e42d1 100644
--- a/FunctionalProject/FunctionalProject.csproj
+++ b/FunctionalProject/FunctionalProject.csproj
@@ -8,9 +8,7 @@
-
-
-
+
diff --git a/FunctionalProject/Program.cs b/FunctionalProject/Program.cs
index 7eae806..7827fdb 100644
--- a/FunctionalProject/Program.cs
+++ b/FunctionalProject/Program.cs
@@ -1,19 +1,16 @@
namespace FunctionalProject
{
- using System.IO;
+ using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
public class Program
{
public static void Main(string[] args)
{
- var host = new WebHostBuilder()
- .UseContentRoot(Directory.GetCurrentDirectory())
- .UseKestrel()
+ WebHost.CreateDefaultBuilder(args)
.UseStartup()
- .Build();
-
- host.Run();
+ .Build()
+ .Run();
}
}
}
diff --git a/FunctionalProject/Startup.cs b/FunctionalProject/Startup.cs
index 26f4203..d652cfd 100644
--- a/FunctionalProject/Startup.cs
+++ b/FunctionalProject/Startup.cs
@@ -10,7 +10,7 @@ public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
- services.AddBotwin(Assembly.GetEntryAssembly(), typeof(FilmValidator).Assembly);
+ services.AddBotwin(Assembly.GetCallingAssembly(), typeof(FilmValidator).Assembly);
}
public void Configure(IApplicationBuilder app)
diff --git a/FunctionalProjectTests/Features/Films/FilmTests.cs b/FunctionalProjectTests/Features/Films/FilmTests.cs
new file mode 100644
index 0000000..192903a
--- /dev/null
+++ b/FunctionalProjectTests/Features/Films/FilmTests.cs
@@ -0,0 +1,115 @@
+namespace FunctionalProjectTests.Features.Films
+{
+ using System;
+ using System.Net.Http;
+ using System.Text;
+ using System.Threading.Tasks;
+ using Botwin;
+ using FunctionalProject.Features.NamedDelegatesFilms.Films;
+ using FunctionalProject.Features.NamedDelegatesFilms.Films.CreateFilm;
+ using FunctionalProject.Features.NamedDelegatesFilms.Films.ListFilmById;
+ using Microsoft.AspNetCore;
+ using Microsoft.AspNetCore.Hosting;
+ using Microsoft.AspNetCore.TestHost;
+ using Models;
+ using Newtonsoft.Json;
+ using Xunit;
+
+ public class FilmTests
+ {
+ private TestServer server;
+
+ private HttpClient client;
+
+ public FilmTests()
+ {
+ server = new TestServer(WebHost.CreateDefaultBuilder()
+ .ConfigureServices(services => services.AddBotwin(typeof(RouteHandlers).Assembly, typeof(FilmValidator).Assembly))
+ .Configure(app => app.UseBotwin())
+ );
+
+ client = server.CreateClient();
+ }
+
+ [Fact]
+ public async Task Should_return_422_on_invalid_data_when_creating_film()
+ {
+ //Given
+
+ //No mock library required to fake the UserValidQuery we just invoke a func to return true/false
+
+ RouteHandlers.CreateFilmHandler = film => CreateFilmRoute.Handle(film, () => true);
+
+ var newFilm = new Film { Name = "" };
+
+ //When
+ var response = await client.PostAsync("/api/delegate/films", new StringContent(JsonConvert.SerializeObject(newFilm), Encoding.UTF8, "application/json"));
+
+ //Then
+ Assert.Equal(422, (int)response.StatusCode);
+ }
+
+ [Fact]
+ public async Task Should_return_403_on_invalid_user_when_creating_film()
+ {
+ //Given
+ RouteHandlers.CreateFilmHandler = film => CreateFilmRoute.Handle(film, () => false);
+
+ var newFilm = new Film { Name = "Shrek" };
+
+ //When
+ var response = await client.PostAsync("/api/delegate/films", new StringContent(JsonConvert.SerializeObject(newFilm), Encoding.UTF8, "application/json"));
+
+ //Then
+ Assert.Equal(403, (int)response.StatusCode);
+ }
+
+ [Fact]
+ public async Task Should_return_201_when_creating_film()
+ {
+ //Given
+ RouteHandlers.CreateFilmHandler = film => CreateFilmRoute.Handle(film, () => true);
+
+ var newFilm = new Film { Name = "Shrek" };
+
+ //When
+ var response = await client.PostAsync("/api/delegate/films", new StringContent(JsonConvert.SerializeObject(newFilm), Encoding.UTF8, "application/json"));
+
+ //Then
+ Assert.Equal(201, (int)response.StatusCode);
+ }
+
+ [Fact]
+ public async Task Should_get_film_by_id()
+ {
+ //Given
+
+ //No mock library required to fake the GetFilmBtIdDelegate, GetDirectorById, GetCastMembersByFilmId we just invoke a func
+
+ RouteHandlers.ListFilmByIdHandler = id => ListFilmByIdRoute.Handle(id, filmid => new Film { Name = "Blade Runner" }, i => new Director(), filmId => new[] { new CastMember() });
+
+ //When
+ var response = await client.GetAsync("/api/delegate/films/1");
+ var contents = await response.Content.ReadAsStringAsync();
+
+ //Then
+ Assert.Contains("Blade Runner", contents, StringComparison.OrdinalIgnoreCase);
+ }
+
+ [Fact]
+ public async Task Should_return_404_when_no_film_found_via_get_film_by_id()
+ {
+ //Given
+
+ //No mock library required to fake the GetFilmBtIdDelegate, GetDirectorById, GetCastMembersByFilmId we just invoke a func
+
+ RouteHandlers.ListFilmByIdHandler = id => ListFilmByIdRoute.Handle(id, filmid => null, i => new Director(), filmId => new[] { new CastMember() });
+
+ //When
+ var response = await client.GetAsync("/api/delegate/films/1");
+
+ //Then
+ Assert.Equal(404, (int)response.StatusCode);
+ }
+ }
+}
diff --git a/FunctionalProjectTests/FunctionalProjectTests.csproj b/FunctionalProjectTests/FunctionalProjectTests.csproj
new file mode 100644
index 0000000..268f38e
--- /dev/null
+++ b/FunctionalProjectTests/FunctionalProjectTests.csproj
@@ -0,0 +1,15 @@
+
+
+ Exe
+ netcoreapp2.0
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MediatRWebAPI.Tests/Features/Films/CreateFilm/CreateFilmMessageHandlerTests.cs b/MediatRWebAPI.Tests/Features/Films/CreateFilm/CreateFilmMessageHandlerTests.cs
new file mode 100644
index 0000000..3464e92
--- /dev/null
+++ b/MediatRWebAPI.Tests/Features/Films/CreateFilm/CreateFilmMessageHandlerTests.cs
@@ -0,0 +1,28 @@
+namespace MediatRWebAPI.Tests.Features.Films.CreateFilm
+{
+ using System;
+ using FakeItEasy;
+ using MediatRWebAPI.Features.Films.CreateFilm;
+ using MediatRWebAPI.Features.Permissions;
+ using Models;
+ using Xunit;
+
+ public class CreateFilmMessageHandlerTests
+ {
+ //This class can be unit test and/or integration test as our handler class is responsible for doing whatever it takes
+
+ [Fact]
+ public void Should_throw_exception_if_user_invalid()
+ {
+ //Given
+ var fakeValidUserQuery = A.Fake();
+ A.CallTo(() => fakeValidUserQuery.Execute()).Returns(false);
+ var handler = new CreateFilmMessageHandler(fakeValidUserQuery);
+
+ //When & Then
+ Assert.Throws(() => handler.Handle(new CreateFilmMessage(new Film())));
+ }
+
+ //Could write an integration test to see if the database has a new film in it or use the API tests to POST then GET the film it created
+ }
+}
diff --git a/MediatRWebAPI.Tests/Features/Films/DeleteFilm/DeleteFilmMessageHandlerTests.cs b/MediatRWebAPI.Tests/Features/Films/DeleteFilm/DeleteFilmMessageHandlerTests.cs
new file mode 100644
index 0000000..6da07fb
--- /dev/null
+++ b/MediatRWebAPI.Tests/Features/Films/DeleteFilm/DeleteFilmMessageHandlerTests.cs
@@ -0,0 +1,27 @@
+namespace MediatRWebAPI.Tests.Features.Films.DeleteFilm
+{
+ using System;
+ using FakeItEasy;
+ using MediatRWebAPI.Features.Films.DeleteFilm;
+ using MediatRWebAPI.Features.Permissions;
+ using Xunit;
+
+ public class DeleteFilmMessageHandlerTests
+ {
+ //This class can be unit test and/or integration test as our handler class is responsible for doing whatever it takes
+
+ [Fact]
+ public void Should_throw_exception_if_user_invalid()
+ {
+ //Given
+ var fakeValidUserQuery = A.Fake();
+ A.CallTo(() => fakeValidUserQuery.Execute()).Returns(false);
+ var handler = new DeleteFilmMessageHandler(fakeValidUserQuery);
+
+ //When & Then
+ Assert.Throws(() => handler.Handle(new DeleteFilmMessage(1)));
+ }
+
+ //Could write an integration test to see if the database has a new film in it or use the API tests to POST then GET the film it created
+ }
+}
diff --git a/MediatRWebAPI.Tests/FilmControllerTests.cs b/MediatRWebAPI.Tests/Features/Films/FilmControllerTests.cs
similarity index 99%
rename from MediatRWebAPI.Tests/FilmControllerTests.cs
rename to MediatRWebAPI.Tests/Features/Films/FilmControllerTests.cs
index 6482730..30635ce 100644
--- a/MediatRWebAPI.Tests/FilmControllerTests.cs
+++ b/MediatRWebAPI.Tests/Features/Films/FilmControllerTests.cs
@@ -1,4 +1,4 @@
-namespace MediatRWebAPI.Tests
+namespace MediatRWebAPI.Tests.Features.Films
{
using System;
using System.Net.Http;
diff --git a/MediatRWebAPI.Tests/Features/Films/ListFilmById/ListFilmsByIdMessageHandlerTests.cs b/MediatRWebAPI.Tests/Features/Films/ListFilmById/ListFilmsByIdMessageHandlerTests.cs
new file mode 100644
index 0000000..5b484a5
--- /dev/null
+++ b/MediatRWebAPI.Tests/Features/Films/ListFilmById/ListFilmsByIdMessageHandlerTests.cs
@@ -0,0 +1,36 @@
+namespace MediatRWebAPI.Tests.Features.Films.ListFilmById
+{
+ using FakeItEasy;
+ using MediatRWebAPI.Features.CastMembers.GetCastByFilmIdQuery;
+ using MediatRWebAPI.Features.Directors.GetDirectorByIdQuery;
+ using MediatRWebAPI.Features.Films.ListFilmById;
+ using MediatRWebAPI.Features.Films.ListFilmByIdQuery;
+ using Xunit;
+
+ public class ListFilmsByIdMessageHandlerTests
+ {
+ //This class can be unit test and/or integration test as our handler class is responsible for doing whatever it takes
+
+ [Fact]
+ public void Should_return_film()
+ {
+ //Given
+ var fakeListFilmByIdQuery = A.Fake();
+ var fakeGetDirectorByIdQuery = A.Fake();
+ var fakeGetCastByIdDirectory = A.Fake();
+ var handler = new ListFilmsByIdMessageHandler(fakeListFilmByIdQuery, fakeGetDirectorByIdQuery, fakeGetCastByIdDirectory);
+
+ //When
+ var film = handler.Handle(new ListFilmsByIdMessage(1));
+
+ //Then
+ Assert.NotNull(film);
+
+ //The issue here, like the service tests are that we are testing the fakes are called which doesn't offer much value
+
+ //If there is a bit more logic in the function then a unit test can be valuable, we can see that in the Create/Update/Delete handlers that do a valid user check
+
+ //You are better off having an integration test at this point possibly with an in-memory sql database or Before/After xUnit attribute to setup db state
+ }
+ }
+}
diff --git a/MediatRWebAPI.Tests/Features/Films/ListFilms/ListFilmsMessageHandlerTests.cs b/MediatRWebAPI.Tests/Features/Films/ListFilms/ListFilmsMessageHandlerTests.cs
new file mode 100644
index 0000000..57316c1
--- /dev/null
+++ b/MediatRWebAPI.Tests/Features/Films/ListFilms/ListFilmsMessageHandlerTests.cs
@@ -0,0 +1,24 @@
+namespace MediatRWebAPI.Tests.Features.Films.ListFilms
+{
+ using System.Linq;
+ using MediatRWebAPI.Features.Films.ListFilms;
+ using Xunit;
+
+ public class ListFilmsMessageHandlerTests
+ {
+ //This class can be unit test and/or integration test as our handler class is responsible for doing whatever it takes
+
+ [Fact]
+ public void Should_return_list_of_films()
+ {
+ //Given
+ var handler = new ListFilmsMessageHandler();
+
+ //When
+ var films = handler.Handle(new ListFilmsMessage());
+
+ //Then
+ Assert.True(films.Any());
+ }
+ }
+}
diff --git a/MediatRWebAPI.Tests/Features/Films/UpdateFilm/UpdateFilmMessageHandlerTests.cs b/MediatRWebAPI.Tests/Features/Films/UpdateFilm/UpdateFilmMessageHandlerTests.cs
new file mode 100644
index 0000000..e7ca548
--- /dev/null
+++ b/MediatRWebAPI.Tests/Features/Films/UpdateFilm/UpdateFilmMessageHandlerTests.cs
@@ -0,0 +1,31 @@
+namespace MediatRWebAPI.Tests.Features.Films.UpdateFilm
+{
+ using System;
+ using FakeItEasy;
+ using MediatRWebAPI.Features.Films.ListFilmByIdQuery;
+ using MediatRWebAPI.Features.Films.UpdateFilm;
+ using MediatRWebAPI.Features.Permissions;
+ using Models;
+ using Xunit;
+
+ public class UpdateFilmMessageHandlerTests
+ {
+ //This class can be unit test and/or integration test as our handler class is responsible for doing whatever it takes
+
+ [Fact]
+ public void Should_throw_exception_if_user_invalid()
+ {
+ //Given
+ var fakeValidUserQuery = A.Fake();
+ A.CallTo(() => fakeValidUserQuery.Execute()).Returns(false);
+ var fakeListFilmByIdQuery = A.Fake();
+
+ var handler = new UpdateFilmMessageHandler(fakeListFilmByIdQuery, fakeValidUserQuery);
+
+ //When & Then
+ Assert.Throws(() => handler.Handle(new UpdateFilmMessage(1, new Film())));
+ }
+
+ //Could write an integration test to see if the database has a new film in it or use the API tests to POST then GET the film it created
+ }
+}
diff --git a/MediatRWebAPI/Features/Films/ListFilmById/ListFilmsByIdMessageHandler.cs b/MediatRWebAPI/Features/Films/ListFilmById/ListFilmsByIdMessageHandler.cs
index 9dd40fd..f1b868e 100644
--- a/MediatRWebAPI/Features/Films/ListFilmById/ListFilmsByIdMessageHandler.cs
+++ b/MediatRWebAPI/Features/Films/ListFilmById/ListFilmsByIdMessageHandler.cs
@@ -25,8 +25,6 @@ public ListFilmsByIdMessageHandler(IListFilmByIdQuery listFilmByIdQuery, IGetDir
public Film Handle(ListFilmsByIdMessage message)
{
- //TODO Unit testing?
-
//Use shared query to get film
var film = this.listFilmByIdQuery.Execute(message.Id);
diff --git a/TradtionalWebAPI.Tests/FilmControllerTests.cs b/TradtionalWebAPI.Tests/Controllers/FilmControllerTests.cs
similarity index 99%
rename from TradtionalWebAPI.Tests/FilmControllerTests.cs
rename to TradtionalWebAPI.Tests/Controllers/FilmControllerTests.cs
index 7ed11ed..f578f2d 100644
--- a/TradtionalWebAPI.Tests/FilmControllerTests.cs
+++ b/TradtionalWebAPI.Tests/Controllers/FilmControllerTests.cs
@@ -1,4 +1,4 @@
-namespace TradtionalWebAPI.Tests
+namespace TradtionalWebAPI.Tests.Controllers
{
using System;
using System.Net.Http;
diff --git a/TradtionalWebAPI.Tests/FilmServiceTests.cs b/TradtionalWebAPI.Tests/Services/FilmServiceTests.cs
similarity index 99%
rename from TradtionalWebAPI.Tests/FilmServiceTests.cs
rename to TradtionalWebAPI.Tests/Services/FilmServiceTests.cs
index e547c10..b244387 100644
--- a/TradtionalWebAPI.Tests/FilmServiceTests.cs
+++ b/TradtionalWebAPI.Tests/Services/FilmServiceTests.cs
@@ -1,4 +1,4 @@
-namespace TradtionalWebAPI.Tests
+namespace TradtionalWebAPI.Tests.Services
{
using System;
using FakeItEasy;