Add tests for controller. Put in contigency for when an item isn't found.

This commit is contained in:
Robert Marshall 2020-04-10 10:01:27 +01:00
parent a22f8b125c
commit c0c1311a0a
6 changed files with 119 additions and 11 deletions

View 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>();
}
}
}

View 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>

View file

@ -16,6 +16,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
..\.editorconfig = ..\.editorconfig
EndProjectSection
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
GlobalSection(SolutionConfigurationPlatforms) = preSolution
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}.Release|Any CPU.ActiveCfg = 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
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

@ -3,6 +3,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Robware.Blog;
using Robware.Data;
namespace Robware.Api.Blog.Controllers {
[ApiController]
@ -16,15 +17,21 @@ namespace Robware.Api.Blog.Controllers {
}
[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}");
try {
if (int.TryParse(url, out int id)) {
return await _blogRepository.GetPostByIdAsync(id);
}
return await _blogRepository.GetPostByUrlAsync(url);
}
catch (ItemNotFoundException e) {
_logger.Log(LogLevel.Error, e.Message);
return NotFound("Could not find blog post");
}
}
//[HttpGet]
//Task<BlogPost> GetPostByUrl();

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using Dapper;
@ -23,11 +24,20 @@ namespace Robware.Data
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) {
const string query = "SELECT * FROM blog_posts WHERE post_url=@url AND post_deleted=0";
using (var connection = _dbProvider.NewConnection()) {
connection.Open();
var result = await connection.QueryAsync<BlogPostState>(query, new{url});
var result = await DoQuery<BlogPostState>(connection, query, new{url});
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";
using (var connection = _dbProvider.NewConnection()) {
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);
}
}
@ -48,7 +58,7 @@ namespace Robware.Data
var query="SELECT COUNT(*) FROM blog_posts WHERE post_content<>'' AND post_deleted=0";
using(var connection = _dbProvider.NewConnection()) {
connection.Open();
var result = await connection.QueryAsync<int>(query);
var result = await DoQuery<int>(connection, query);
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";
using (var connection = _dbProvider.NewConnection()) {
connection.Open();
var result = await connection.QueryAsync<BlogPostState>(query, new {id});
var result = await DoQuery<BlogPostState>(connection, query, new {id});
return MapBlogPost(result.First());
}
}
@ -70,7 +80,7 @@ namespace Robware.Data
using (var connection = _dbProvider.NewConnection()) {
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;
}
}
@ -80,7 +90,7 @@ namespace Robware.Data
using (var connection = _dbProvider.NewConnection()) {
connection.Open();
var result = await connection.QueryAsync<BlogPostState>(query);
var result = await DoQuery<BlogPostState>(connection, query);
return result.Select(MapBlogPost);
}
}

View 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;
}
}
}