Add tests for controller. Put in contigency for when an item isn't found.
This commit is contained in:
parent
a22f8b125c
commit
c0c1311a0a
6 changed files with 119 additions and 11 deletions
52
src/Robware.Api.Blog.Tests/BlogControllerTests.cs
Normal file
52
src/Robware.Api.Blog.Tests/BlogControllerTests.cs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using FluentAssertions;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using NSubstitute;
|
||||||
|
using NSubstitute.ExceptionExtensions;
|
||||||
|
using Robware.Api.Blog.Controllers;
|
||||||
|
using Robware.Blog;
|
||||||
|
using Robware.Data;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Robware.Api.Blog.Tests {
|
||||||
|
public class BlogControllerTests {
|
||||||
|
[Fact]
|
||||||
|
public async Task Get_WithUrl_ReturnsBlogPost() {
|
||||||
|
var logger = Substitute.For<ILogger<BlogController>>();
|
||||||
|
var repo = Substitute.For<IBlogRepository>();
|
||||||
|
|
||||||
|
repo.GetPostByUrlAsync("url").Returns(new BlogPost());
|
||||||
|
|
||||||
|
var expectation = new BlogPost();
|
||||||
|
|
||||||
|
var controller = new BlogController(logger, repo);
|
||||||
|
(await controller.Get("url")).Value.Should().BeEquivalentTo(expectation);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Get_WithId_ReturnsBlogPost() {
|
||||||
|
var logger = Substitute.For<ILogger<BlogController>>();
|
||||||
|
var repo = Substitute.For<IBlogRepository>();
|
||||||
|
|
||||||
|
repo.GetPostByIdAsync(1).Returns(new BlogPost());
|
||||||
|
|
||||||
|
var expectation = new BlogPost();
|
||||||
|
|
||||||
|
var controller = new BlogController(logger, repo);
|
||||||
|
(await controller.Get("1")).Value.Should().BeEquivalentTo(expectation);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Get_WithInvalidUrl_Returns404() {
|
||||||
|
var logger = Substitute.For<ILogger<BlogController>>();
|
||||||
|
var repo = Substitute.For<IBlogRepository>();
|
||||||
|
|
||||||
|
repo.GetPostByUrlAsync("url").Throws(new ItemNotFoundException("", null));
|
||||||
|
|
||||||
|
var controller = new BlogController(logger, repo);
|
||||||
|
(await controller.Get("url")).Result.Should().BeOfType<NotFoundObjectResult>();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
src/Robware.Api.Blog.Tests/Robware.Api.Blog.Tests.csproj
Normal file
22
src/Robware.Api.Blog.Tests/Robware.Api.Blog.Tests.csproj
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="FluentAssertions" Version="5.10.3" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
|
||||||
|
<PackageReference Include="NSubstitute" Version="4.2.1" />
|
||||||
|
<PackageReference Include="xunit" Version="2.4.0" />
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
|
||||||
|
<PackageReference Include="coverlet.collector" Version="1.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Robware.Api.Blog\Robware.Api.Blog.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -16,6 +16,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||||
..\.editorconfig = ..\.editorconfig
|
..\.editorconfig = ..\.editorconfig
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robware.Api.Blog.Tests", "Robware.Api.Blog.Tests\Robware.Api.Blog.Tests.csproj", "{54DEC274-5F28-42FB-9B60-1E4BAF3D7BF9}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -38,6 +40,10 @@ Global
|
||||||
{EEB41C17-B61C-451A-9C72-2B405DC42743}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{EEB41C17-B61C-451A-9C72-2B405DC42743}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{EEB41C17-B61C-451A-9C72-2B405DC42743}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{EEB41C17-B61C-451A-9C72-2B405DC42743}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{EEB41C17-B61C-451A-9C72-2B405DC42743}.Release|Any CPU.Build.0 = Release|Any CPU
|
{EEB41C17-B61C-451A-9C72-2B405DC42743}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{54DEC274-5F28-42FB-9B60-1E4BAF3D7BF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{54DEC274-5F28-42FB-9B60-1E4BAF3D7BF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{54DEC274-5F28-42FB-9B60-1E4BAF3D7BF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{54DEC274-5F28-42FB-9B60-1E4BAF3D7BF9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -3,6 +3,7 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Robware.Blog;
|
using Robware.Blog;
|
||||||
|
using Robware.Data;
|
||||||
|
|
||||||
namespace Robware.Api.Blog.Controllers {
|
namespace Robware.Api.Blog.Controllers {
|
||||||
[ApiController]
|
[ApiController]
|
||||||
|
@ -16,14 +17,20 @@ namespace Robware.Api.Blog.Controllers {
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet(nameof(Get) + "/{url}")]
|
[HttpGet(nameof(Get) + "/{url}")]
|
||||||
public async Task<BlogPost> Get(string url) {
|
public async Task<ActionResult<BlogPost>> Get(string url) {
|
||||||
_logger.Log(LogLevel.Information, $"{nameof(Get)}: {nameof(url)}={url}");
|
_logger.Log(LogLevel.Information, $"{nameof(Get)}: {nameof(url)}={url}");
|
||||||
|
|
||||||
if (int.TryParse(url, out int id)) {
|
try {
|
||||||
return await _blogRepository.GetPostByIdAsync(id);
|
if (int.TryParse(url, out int id)) {
|
||||||
}
|
return await _blogRepository.GetPostByIdAsync(id);
|
||||||
|
}
|
||||||
|
|
||||||
return await _blogRepository.GetPostByUrlAsync(url);
|
return await _blogRepository.GetPostByUrlAsync(url);
|
||||||
|
}
|
||||||
|
catch (ItemNotFoundException e) {
|
||||||
|
_logger.Log(LogLevel.Error, e.Message);
|
||||||
|
return NotFound("Could not find blog post");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//[HttpGet]
|
//[HttpGet]
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Dapper;
|
using Dapper;
|
||||||
|
@ -23,11 +24,20 @@ namespace Robware.Data
|
||||||
UserId = state.User_Id
|
UserId = state.User_Id
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private async Task<IEnumerable<T>> DoQuery<T>(IDbConnection connection, string query, object param = null) {
|
||||||
|
var result = await connection.QueryAsync<T>(query, param);
|
||||||
|
|
||||||
|
if (!result.Any())
|
||||||
|
throw new ItemNotFoundException(query, param);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<BlogPost> GetPostByUrlAsync(string url) {
|
public async Task<BlogPost> GetPostByUrlAsync(string url) {
|
||||||
const string query = "SELECT * FROM blog_posts WHERE post_url=@url AND post_deleted=0";
|
const string query = "SELECT * FROM blog_posts WHERE post_url=@url AND post_deleted=0";
|
||||||
using (var connection = _dbProvider.NewConnection()) {
|
using (var connection = _dbProvider.NewConnection()) {
|
||||||
connection.Open();
|
connection.Open();
|
||||||
var result = await connection.QueryAsync<BlogPostState>(query, new{url});
|
var result = await DoQuery<BlogPostState>(connection, query, new{url});
|
||||||
return MapBlogPost(result.First());
|
return MapBlogPost(result.First());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +46,7 @@ namespace Robware.Data
|
||||||
const string query = "SELECT * FROM blog_posts WHERE post_content<>'' AND post_deleted=0 ORDER BY post_timestamp DESC LIMIT @offset,@limit";
|
const string query = "SELECT * FROM blog_posts WHERE post_content<>'' AND post_deleted=0 ORDER BY post_timestamp DESC LIMIT @offset,@limit";
|
||||||
using (var connection = _dbProvider.NewConnection()) {
|
using (var connection = _dbProvider.NewConnection()) {
|
||||||
connection.Open();
|
connection.Open();
|
||||||
var results = await connection.QueryAsync<BlogPostState>(query, new{limit, offset});
|
var results = await DoQuery<BlogPostState>(connection, query, new{limit, offset});
|
||||||
return results.Select(MapBlogPost);
|
return results.Select(MapBlogPost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +58,7 @@ namespace Robware.Data
|
||||||
var query="SELECT COUNT(*) FROM blog_posts WHERE post_content<>'' AND post_deleted=0";
|
var query="SELECT COUNT(*) FROM blog_posts WHERE post_content<>'' AND post_deleted=0";
|
||||||
using(var connection = _dbProvider.NewConnection()) {
|
using(var connection = _dbProvider.NewConnection()) {
|
||||||
connection.Open();
|
connection.Open();
|
||||||
var result = await connection.QueryAsync<int>(query);
|
var result = await DoQuery<int>(connection, query);
|
||||||
return result.First();
|
return result.First();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +67,7 @@ namespace Robware.Data
|
||||||
const string query = "SELECT * FROM blog_posts WHERE post_id=@id AND post_deleted=0";
|
const string query = "SELECT * FROM blog_posts WHERE post_id=@id AND post_deleted=0";
|
||||||
using (var connection = _dbProvider.NewConnection()) {
|
using (var connection = _dbProvider.NewConnection()) {
|
||||||
connection.Open();
|
connection.Open();
|
||||||
var result = await connection.QueryAsync<BlogPostState>(query, new {id});
|
var result = await DoQuery<BlogPostState>(connection, query, new {id});
|
||||||
return MapBlogPost(result.First());
|
return MapBlogPost(result.First());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +80,7 @@ namespace Robware.Data
|
||||||
|
|
||||||
using (var connection = _dbProvider.NewConnection()) {
|
using (var connection = _dbProvider.NewConnection()) {
|
||||||
connection.Open();
|
connection.Open();
|
||||||
var result = await connection.QueryAsync<int>(newPost ? newPostQuery : updatePostQuery, post);
|
var result = await DoQuery<int>(connection, newPost ? newPostQuery : updatePostQuery, post);
|
||||||
return newPost ? await GetPostByIdAsync(result.Single()) : post;
|
return newPost ? await GetPostByIdAsync(result.Single()) : post;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +90,7 @@ namespace Robware.Data
|
||||||
|
|
||||||
using (var connection = _dbProvider.NewConnection()) {
|
using (var connection = _dbProvider.NewConnection()) {
|
||||||
connection.Open();
|
connection.Open();
|
||||||
var result = await connection.QueryAsync<BlogPostState>(query);
|
var result = await DoQuery<BlogPostState>(connection, query);
|
||||||
return result.Select(MapBlogPost);
|
return result.Select(MapBlogPost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
11
src/Robware.Data/ItemNotFoundException.cs
Normal file
11
src/Robware.Data/ItemNotFoundException.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Robware.Data {
|
||||||
|
public class ItemNotFoundException:Exception {
|
||||||
|
public object Parameters { get; }
|
||||||
|
|
||||||
|
public ItemNotFoundException(string query, object parameters):base($"Could not find an item for the query: {query}") {
|
||||||
|
Parameters = parameters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue