diff --git a/PineBlog.sln b/PineBlog.sln index 6c9935d..53a716a 100644 --- a/PineBlog.sln +++ b/PineBlog.sln @@ -58,6 +58,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01 Docs", "01 Docs", "{5D82 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Opw.PineBlog.EntityFrameworkCore.Tests", "tests\Opw.PineBlog.EntityFrameworkCore.Tests\Opw.PineBlog.EntityFrameworkCore.Tests.csproj", "{09215473-245F-4484-B61C-7FBFC70E8C18}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Opw.PineBlog.Sample.Tests", "tests\Opw.PineBlog.Sample.Tests\Opw.PineBlog.Sample.Tests.csproj", "{FD61CF8C-B52E-4692-9B93-8D48255FD3D1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -104,6 +106,10 @@ Global {09215473-245F-4484-B61C-7FBFC70E8C18}.Debug|Any CPU.Build.0 = Debug|Any CPU {09215473-245F-4484-B61C-7FBFC70E8C18}.Release|Any CPU.ActiveCfg = Release|Any CPU {09215473-245F-4484-B61C-7FBFC70E8C18}.Release|Any CPU.Build.0 = Release|Any CPU + {FD61CF8C-B52E-4692-9B93-8D48255FD3D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FD61CF8C-B52E-4692-9B93-8D48255FD3D1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FD61CF8C-B52E-4692-9B93-8D48255FD3D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FD61CF8C-B52E-4692-9B93-8D48255FD3D1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -119,6 +125,7 @@ Global {49EA4528-BA74-4FE9-862D-BC1448D50EF8} = {FCEC3C6B-0FC0-42CC-AEE8-EBBC570A67A0} {122A1302-2B62-485F-B80F-3E9210B0616F} = {64F9A954-9830-45BA-B47F-D3589072E347} {09215473-245F-4484-B61C-7FBFC70E8C18} = {F800AFAF-471F-41F4-8F17-0C4FAEC41F6A} + {FD61CF8C-B52E-4692-9B93-8D48255FD3D1} = {5FEBABA0-73A1-464A-8715-19F76F742B22} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DF018025-4A97-49F8-888F-C70856C1862A} diff --git a/README.md b/README.md index ae5a9cd..c6d5d4a 100644 --- a/README.md +++ b/README.md @@ -57,8 +57,21 @@ public void ConfigureServices(IServiceCollection services) ... services.AddPineBlog(Configuration); - services.AddMvc().AddPineBlogRazorPages(); + services.AddRazorPages().AddPineBlogRazorPages(); // or services.AddMvcCore().AddPineBlogRazorPages(); + // or services.AddMvc().AddPineBlogRazorPages(); + ... +} + +public void Configure(IApplicationBuilder app, IWebHostEnvironment env) +{ + ... + app.UseEndpoints(endpoints => + { + // make sure to add the endpoint mapping for both RazorPages and Controllers + endpoints.MapRazorPages(); + endpoints.MapControllers(); + }); ... } ``` diff --git a/docs/getting-started.md b/docs/getting-started.md index 4687f4f..b815c4a 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -7,8 +7,21 @@ public void ConfigureServices(IServiceCollection services) ... services.AddPineBlog(Configuration); - services.AddMvc().AddPineBlogRazorPages(); + services.AddRazorPages().AddPineBlogRazorPages(); // or services.AddMvcCore().AddPineBlogRazorPages(); + // or services.AddMvc().AddPineBlogRazorPages(); + ... +} + +public void Configure(IApplicationBuilder app, IWebHostEnvironment env) +{ + ... + app.UseEndpoints(endpoints => + { + // make sure to add the endpoint mapping for both RazorPages and Controllers + endpoints.MapRazorPages(); + endpoints.MapControllers(); + }); ... } ``` diff --git a/samples/Opw.PineBlog.Sample/Opw.PineBlog.Sample.csproj b/samples/Opw.PineBlog.Sample/Opw.PineBlog.Sample.csproj index 3ebde33..74e469f 100644 --- a/samples/Opw.PineBlog.Sample/Opw.PineBlog.Sample.csproj +++ b/samples/Opw.PineBlog.Sample/Opw.PineBlog.Sample.csproj @@ -15,7 +15,6 @@ - diff --git a/samples/Opw.PineBlog.Sample/Program.cs b/samples/Opw.PineBlog.Sample/Program.cs index 06ae0d7..c8c544b 100644 --- a/samples/Opw.PineBlog.Sample/Program.cs +++ b/samples/Opw.PineBlog.Sample/Program.cs @@ -34,9 +34,7 @@ public static void Main(string[] args) public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup() - .ConfigureAppConfiguration((hostingContext, config) => { - config.AddPineBlogConfiguration(reloadOnChange: true); - }) + .ConfigureAppConfiguration((_, config) => config.AddPineBlogConfiguration(reloadOnChange: true)) .ConfigureLogging((hostingContext, builder) => { builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); diff --git a/samples/Opw.PineBlog.Sample/Startup.cs b/samples/Opw.PineBlog.Sample/Startup.cs index de0ecae..54bd050 100644 --- a/samples/Opw.PineBlog.Sample/Startup.cs +++ b/samples/Opw.PineBlog.Sample/Startup.cs @@ -39,7 +39,8 @@ public void ConfigureServices(IServiceCollection services) // TODO: combine with AddPineBlogRazorPages? services.AddPineBlog(Configuration); - services.AddRazorPages() + services + .AddRazorPages() .SetCompatibilityVersion(CompatibilityVersion.Version_3_0) .AddPineBlogRazorPages(); } @@ -52,7 +53,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); - //TODO: app.UseDatabaseErrorPage(); } else { @@ -68,7 +68,11 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); - app.UseEndpoints(endpoints => endpoints.MapRazorPages()); + app.UseEndpoints(endpoints => + { + endpoints.MapRazorPages(); + endpoints.MapControllers(); + }); } } } diff --git a/tests/Opw.PineBlog.Core.Tests/Constants.cs b/tests/Opw.PineBlog.Core.Tests/Constants.cs new file mode 100644 index 0000000..7b62755 --- /dev/null +++ b/tests/Opw.PineBlog.Core.Tests/Constants.cs @@ -0,0 +1,8 @@ + +namespace Opw.PineBlog +{ + public static class Constants + { + public const string SkipAzureStorageEmulatorTests = "Requires Azure Storage Emulator."; + } +} diff --git a/tests/Opw.PineBlog.Core.Tests/Files/Azure/DeleteAzureBlobCommandTests.cs b/tests/Opw.PineBlog.Core.Tests/Files/Azure/DeleteAzureBlobCommandTests.cs index 3e3fbf0..bf3d5c2 100644 --- a/tests/Opw.PineBlog.Core.Tests/Files/Azure/DeleteAzureBlobCommandTests.cs +++ b/tests/Opw.PineBlog.Core.Tests/Files/Azure/DeleteAzureBlobCommandTests.cs @@ -38,7 +38,7 @@ public async Task Validator_Should_ThrowValidationErrorException() ex.Errors.Single(e => e.Key.Equals(nameof(DeleteAzureBlobCommand.FileName))).Should().NotBeNull(); } - [Fact(Skip = "Integration Test; requires Azure Storage Emulator.")] + [Fact(Skip = Constants.SkipAzureStorageEmulatorTests)] public async Task Handler_Should_ReturnTrue() { await Mediator.Send(new UploadAzureBlobCommand { File = _formFileMock.Object, TargetPath = "files", AllowedFileType = FileType.All }); @@ -48,7 +48,7 @@ public async Task Handler_Should_ReturnTrue() result.IsSuccess.Should().BeTrue(); } - [Fact(Skip = "Integration Test; requires Azure Storage Emulator.")] + [Fact(Skip = Constants.SkipAzureStorageEmulatorTests)] public async Task Handler_Should_ReturnFalse_WhenFileDoesNotExist() { var result = await Mediator.Send(new DeleteAzureBlobCommand { FileName = "invalid-filename.txt", TargetPath = "files" }); diff --git a/tests/Opw.PineBlog.Core.Tests/Files/Azure/GetPagedAzureBlobListQueryTest.cs b/tests/Opw.PineBlog.Core.Tests/Files/Azure/GetPagedAzureBlobListQueryTest.cs index cb09ac3..e8267d2 100644 --- a/tests/Opw.PineBlog.Core.Tests/Files/Azure/GetPagedAzureBlobListQueryTest.cs +++ b/tests/Opw.PineBlog.Core.Tests/Files/Azure/GetPagedAzureBlobListQueryTest.cs @@ -10,7 +10,7 @@ namespace Opw.PineBlog.Files.Azure { public class GetPagedAzureBlobListQueryTest : MediatRTestsBase { - [Fact(Skip = "Integration Test; requires Azure Storage Emulator.")] + [Fact(Skip = Constants.SkipAzureStorageEmulatorTests)] public async Task Handler_Should_ReturnFirstPageWith5Files() { var directory = $"{DateTime.UtcNow.Ticks}-{Guid.NewGuid()}"; @@ -28,7 +28,7 @@ public async Task Handler_Should_ReturnFirstPageWith5Files() result.Value.Pager.Total.Should().Be(9); } - [Fact(Skip = "Integration Test; requires Azure Storage Emulator.")] + [Fact(Skip = Constants.SkipAzureStorageEmulatorTests)] public async Task Handler_Should_ReturnSecondPageWith4Files() { var directory = $"{DateTime.UtcNow.Ticks}-{Guid.NewGuid()}"; @@ -46,7 +46,7 @@ public async Task Handler_Should_ReturnSecondPageWith4Files() result.Value.Pager.Total.Should().Be(9); } - [Fact(Skip = "Integration Test; requires Azure Storage Emulator.")] + [Fact(Skip = Constants.SkipAzureStorageEmulatorTests)] public async Task Handler_Should_Return3Files_ForFileTypeImage() { var directory = $"{DateTime.UtcNow.Ticks}-{Guid.NewGuid()}"; diff --git a/tests/Opw.PineBlog.Core.Tests/Files/Azure/UploadAzureBlobCommandTests.cs b/tests/Opw.PineBlog.Core.Tests/Files/Azure/UploadAzureBlobCommandTests.cs index 109da64..2df543b 100644 --- a/tests/Opw.PineBlog.Core.Tests/Files/Azure/UploadAzureBlobCommandTests.cs +++ b/tests/Opw.PineBlog.Core.Tests/Files/Azure/UploadAzureBlobCommandTests.cs @@ -39,7 +39,7 @@ public async Task Validator_Should_ThrowValidationErrorException() ex.Errors.Single(e => e.Key.Equals(nameof(UploadAzureBlobCommand.AllowedFileType))).Should().NotBeNull(); } - [Fact(Skip = "Integration Test; requires Azure Storage Emulator.")] + [Fact(Skip = Constants.SkipAzureStorageEmulatorTests)] public async Task Handler_Should_ReturnTrue() { var result = await Mediator.Send(new UploadAzureBlobCommand { File = _formFileMock.Object, TargetPath = "files", AllowedFileType = FileType.All }); diff --git a/tests/Opw.PineBlog.Sample.Tests/Controllers/FileControllerTests.cs b/tests/Opw.PineBlog.Sample.Tests/Controllers/FileControllerTests.cs new file mode 100644 index 0000000..ce1dff8 --- /dev/null +++ b/tests/Opw.PineBlog.Sample.Tests/Controllers/FileControllerTests.cs @@ -0,0 +1,30 @@ +using Newtonsoft.Json; +using Opw.PineBlog.Models; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace Opw.PineBlog.Sample.Controllers +{ + public class FileControllerTests : IClassFixture + { + private readonly TestWebApplicationFactory _factory; + private readonly HttpClient _client; + + public FileControllerTests(TestWebApplicationFactory factory) + { + _factory = factory; + _client = _factory.CreateClient(); + } + + [Fact] + public async Task Get_Should_BeFound() + { + var response = await _client.GetAsync("admin/file"); + response.EnsureSuccessStatusCode(); + } + } +} diff --git a/tests/Opw.PineBlog.Sample.Tests/Opw.PineBlog.Sample.Tests.csproj b/tests/Opw.PineBlog.Sample.Tests/Opw.PineBlog.Sample.Tests.csproj new file mode 100644 index 0000000..c976181 --- /dev/null +++ b/tests/Opw.PineBlog.Sample.Tests/Opw.PineBlog.Sample.Tests.csproj @@ -0,0 +1,37 @@ + + + + netcoreapp3.0;netcoreapp3.1 + false + Opw.PineBlog.Sample + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + Always + + + + diff --git a/tests/Opw.PineBlog.Sample.Tests/TestWebApplicationFactory.cs b/tests/Opw.PineBlog.Sample.Tests/TestWebApplicationFactory.cs new file mode 100644 index 0000000..ef145a4 --- /dev/null +++ b/tests/Opw.PineBlog.Sample.Tests/TestWebApplicationFactory.cs @@ -0,0 +1,35 @@ +using System; +using System.Net.Http; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Extensions.Configuration; +using Opw.PineBlog.EntityFrameworkCore; + +namespace Opw.PineBlog.Sample +{ + public class TestWebApplicationFactory : WebApplicationFactory + { + public IConfigurationRoot Configuration { get; } + + public TestWebApplicationFactory() + { + Configuration = new ConfigurationBuilder() + .AddJsonFile("appsettings.json") + .Build(); + } + + protected override IWebHostBuilder CreateWebHostBuilder() + { + return new WebHostBuilder() + .UseConfiguration(Configuration) + .UseStartup() + .ConfigureAppConfiguration((_, config) => config.AddPineBlogConfiguration(reloadOnChange: true)); + } + + //public new HttpClient CreateClient() + //{ + // var baseAddress = new Uri($"http://localhost/"); + // return CreateClient(new WebApplicationFactoryClientOptions { BaseAddress = baseAddress }); + //} + } +} diff --git a/tests/Opw.PineBlog.Sample.Tests/appsettings.json b/tests/Opw.PineBlog.Sample.Tests/appsettings.json new file mode 100644 index 0000000..1e47629 --- /dev/null +++ b/tests/Opw.PineBlog.Sample.Tests/appsettings.json @@ -0,0 +1,28 @@ +{ + "ConnectionStrings": { + "DefaultConnection": "Server=inMemory; Database=pineblog-db;" + }, + "PineBlogOptions": { + "Title": "PineBlog", + "Description": "A blogging engine based on ASP.NET Core MVC Razor Pages and Entity Framework Core", + "CoverUrl": "/images/woods.gif", + "CoverCaption": "Battle background for the Misty Woods in the game Shadows of Adam by Tim Wendorf", + "CoverLink": "http://pixeljoint.com/pixelart/94359.htm", + "ItemsPerPage": 2, + "CreateAndSeedDatabases": true, + "ConnectionStringName": "DefaultConnection", + "AzureStorageConnectionString": "UseDevelopmentStorage=true", + "AzureStorageBlobContainerName": "pineblog", + "FileBaseUrl": "http://127.0.0.1:10000/devstoreaccount1" + }, + "Logging": { + "LogLevel": { + "Default": "Information" + } + }, + "AllowedHosts": "*", + "StopApplicationPath": "/stop-app", + "ApplicationInsights": { + "InstrumentationKey": null + } +} \ No newline at end of file