Add unit & integration tests for TokensController.cs

This commit is contained in:
Denis-Cosmin Nutiu 2021-06-30 23:07:39 +03:00
parent 32054fea3c
commit 56bbb68dda
10 changed files with 362 additions and 12 deletions

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Runtime.CompilerServices;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using MongoDB.Bson; using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Attributes;
@ -33,7 +34,7 @@ namespace Retroactiune.Core.Entities
public override int GetHashCode() public override int GetHashCode()
{ {
return base.GetHashCode(); return RuntimeHelpers.GetHashCode(this);
} }
} }
} }

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Runtime.CompilerServices;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using MongoDB.Bson; using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Attributes;
@ -31,13 +32,14 @@ namespace Retroactiune.Core.Entities
return false; return false;
} }
return string.Equals(Id, convertedObj.Id) && string.Equals(FeedbackReceiverId, convertedObj.FeedbackReceiverId) && return string.Equals(Id, convertedObj.Id) &&
TimeUsed == convertedObj.TimeUsed && ExpiryTime == convertedObj.ExpiryTime; string.Equals(FeedbackReceiverId, convertedObj.FeedbackReceiverId) &&
(CreatedAt - convertedObj.CreatedAt).Milliseconds == 0;
} }
public override int GetHashCode() public override int GetHashCode()
{ {
return base.GetHashCode(); return RuntimeHelpers.GetHashCode(this);
} }
} }
} }

View file

@ -56,13 +56,12 @@ namespace Retroactiune.Core.Services
public async Task<IEnumerable<Token>> ListTokens(TokenListFilters filters) public async Task<IEnumerable<Token>> ListTokens(TokenListFilters filters)
{ {
// TODO Write unit tests.
var filterBuilder = new FilterDefinitionBuilder<Token>(); var filterBuilder = new FilterDefinitionBuilder<Token>();
var activeFilters = new List<FilterDefinition<Token>>(); var activeFilters = new List<FilterDefinition<Token>>();
var tokensListFilter = FilterDefinition<Token>.Empty; var tokensListFilter = FilterDefinition<Token>.Empty;
// Filter by token ids. // Filter by token ids.
if (filters.Ids.Any()) if (filters.Ids != null && filters.Ids.Any())
{ {
activeFilters.Add(filterBuilder.In(i => i.Id, filters.Ids)); activeFilters.Add(filterBuilder.In(i => i.Id, filters.Ids));
} }

View file

@ -24,12 +24,12 @@ using JsonSerializer = System.Text.Json.JsonSerializer;
namespace Retroactiune.IntegrationTests.Retroactiune.WebAPI.Controllers namespace Retroactiune.IntegrationTests.Retroactiune.WebAPI.Controllers
{ {
[Collection("IntegrationTests")] [Collection("IntegrationTests")]
public class TestFeedbackReceiver : IClassFixture<WebApiTestingFactory> public class TestFeedbackReceiversController : IClassFixture<WebApiTestingFactory>
{ {
private readonly MongoDbFixture _mongoDb; private readonly MongoDbFixture _mongoDb;
private readonly HttpClient _client; private readonly HttpClient _client;
public TestFeedbackReceiver(WebApiTestingFactory factory) public TestFeedbackReceiversController(WebApiTestingFactory factory)
{ {
_client = factory.CreateClient(); _client = factory.CreateClient();
var dbSettings = factory.Services.GetService<IOptions<DatabaseSettings>>(); var dbSettings = factory.Services.GetService<IOptions<DatabaseSettings>>();

View file

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
@ -7,6 +8,7 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AutoFixture.Xunit2; using AutoFixture.Xunit2;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using MongoDB.Bson; using MongoDB.Bson;
@ -16,16 +18,17 @@ using Retroactiune.Core.Entities;
using Retroactiune.Infrastructure; using Retroactiune.Infrastructure;
using Retroactiune.IntegrationTests.Retroactiune.WebAPI.Fixtures; using Retroactiune.IntegrationTests.Retroactiune.WebAPI.Fixtures;
using Xunit; using Xunit;
using JsonSerializer = System.Text.Json.JsonSerializer;
namespace Retroactiune.IntegrationTests.Retroactiune.WebAPI.Controllers namespace Retroactiune.IntegrationTests.Retroactiune.WebAPI.Controllers
{ {
[Collection("IntegrationTests")] [Collection("IntegrationTests")]
public class TestTokens : IClassFixture<WebApiTestingFactory> public class TestTokensController : IClassFixture<WebApiTestingFactory>
{ {
private readonly MongoDbFixture _mongoDb; private readonly MongoDbFixture _mongoDb;
private readonly HttpClient _client; private readonly HttpClient _client;
public TestTokens(WebApiTestingFactory factory) public TestTokensController(WebApiTestingFactory factory)
{ {
_client = factory.CreateClient(); _client = factory.CreateClient();
var dbSettings = factory.Services.GetService<IOptions<DatabaseSettings>>(); var dbSettings = factory.Services.GetService<IOptions<DatabaseSettings>>();
@ -207,5 +210,168 @@ namespace Retroactiune.IntegrationTests.Retroactiune.WebAPI.Controllers
// Assert // Assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
} }
[Fact]
public async Task Test_ListTokens_NoFilter_Empty()
{
// Setup
await _mongoDb.DropAsync();
// Test
var response = await _client.GetAsync("api/v1/Tokens");
var items = JsonSerializer.Deserialize<List<Token>>(await response.Content.ReadAsStringAsync());
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Empty(items);
}
[Fact]
public async Task Test_ListTokens_NoFilter()
{
// Setup
await _mongoDb.DropAsync();
var timeNow = DateTime.UtcNow;
var tokens = TokensFixture.Generate(10, timeNow);
await _mongoDb.TokensCollection.InsertManyAsync(tokens);
// Test
var response = await _client.GetAsync("api/v1/Tokens");
var items = JsonSerializer.Deserialize<List<Token>>(await response.Content.ReadAsStringAsync());
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(tokens.Count, items.Count);
for (var i = 0; i < tokens.Count; i++)
{
Assert.Equal(tokens[i], items[i]);
}
}
[Fact]
public async Task Test_ListTokens_Filter_FeedbackReceiverId()
{
// Setup
await _mongoDb.DropAsync();
var timeNow = DateTime.UtcNow;
var tokens = TokensFixture.Generate(13, timeNow);
var expectedTokens = TokensFixture.Generate(1, timeNow);
var qb = new QueryBuilder
{
{"FeedbackReceiverId", expectedTokens[0].FeedbackReceiverId},
};
await _mongoDb.TokensCollection.InsertManyAsync(tokens);
await _mongoDb.TokensCollection.InsertManyAsync(expectedTokens);
// Test
var response = await _client.GetAsync($"api/v1/Tokens{qb}");
var items = JsonSerializer.Deserialize<List<Token>>(await response.Content.ReadAsStringAsync());
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Single(items);
Assert.Equal(expectedTokens[0], items[0]);
}
[Fact]
public async Task Test_ListTokens_Filter_Ids()
{
// Setup
await _mongoDb.DropAsync();
var timeNow = DateTime.UtcNow;
var tokens = TokensFixture.Generate(13, timeNow);
var expectedTokens = TokensFixture.Generate(1, timeNow);
var qb = new QueryBuilder
{
{"Ids", expectedTokens[0].Id},
};
await _mongoDb.TokensCollection.InsertManyAsync(tokens);
await _mongoDb.TokensCollection.InsertManyAsync(expectedTokens);
// Test
var response = await _client.GetAsync($"api/v1/Tokens{qb}");
var items = JsonSerializer.Deserialize<List<Token>>(await response.Content.ReadAsStringAsync());
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Single(items);
Assert.Equal(expectedTokens[0], items[0]);
}
[Fact]
public async Task Test_ListTokens_Filter_CreatedRange()
{
// Setup
await _mongoDb.DropAsync();
var timeNow = DateTime.UtcNow;
var oldTokens = TokensFixture.Generate(13, timeNow.AddDays(-10));
var futureTokens = TokensFixture.Generate(13, timeNow.AddDays(10));
var expectedTokens = TokensFixture.Generate(5, timeNow);
var qb = new QueryBuilder
{
{"CreatedAfter", timeNow.AddDays(-3).ToString(CultureInfo.InvariantCulture)},
{"CreatedBefore", timeNow.AddDays(3).ToString(CultureInfo.InvariantCulture)},
};
await _mongoDb.TokensCollection.InsertManyAsync(oldTokens);
await _mongoDb.TokensCollection.InsertManyAsync(expectedTokens);
await _mongoDb.TokensCollection.InsertManyAsync(futureTokens);
// Test
var response = await _client.GetAsync($"api/v1/Tokens{qb}");
var items = JsonSerializer.Deserialize<List<Token>>(await response.Content.ReadAsStringAsync());
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(expectedTokens.Count, items.Count);
for (var i = 0; i < items.Count; i++)
{
Assert.Equal(expectedTokens[i], items[i]);
}
}
[Fact]
public async Task Test_ListTokens_Filter_UsedRange()
{
// Setup
await _mongoDb.DropAsync();
var timeNow = DateTime.UtcNow;
var oldTokens = TokensFixture.Generate(13, timeNow.AddDays(-10));
var futureTokens = TokensFixture.Generate(13, timeNow.AddDays(10));
var expectedTokens = TokensFixture.Generate(5, timeNow, null, null, timeNow);
var qb = new QueryBuilder
{
{"UsedAfter", timeNow.AddDays(-3).ToString(CultureInfo.InvariantCulture)},
{"UsedBefore", timeNow.AddDays(3).ToString(CultureInfo.InvariantCulture)},
};
await _mongoDb.TokensCollection.InsertManyAsync(oldTokens);
await _mongoDb.TokensCollection.InsertManyAsync(expectedTokens);
await _mongoDb.TokensCollection.InsertManyAsync(futureTokens);
// Test
var response = await _client.GetAsync($"api/v1/Tokens{qb}");
var items = JsonSerializer.Deserialize<List<Token>>(await response.Content.ReadAsStringAsync());
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(expectedTokens.Count, items.Count);
for (var i = 0; i < items.Count; i++)
{
Assert.Equal(expectedTokens[i], items[i]);
}
}
} }
} }

View file

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using MongoDB.Bson;
using Retroactiune.Core.Entities;
namespace Retroactiune.IntegrationTests.Retroactiune.WebAPI.Fixtures
{
public static class TokensFixture
{
public static List<Token> Generate(int number, DateTime createdAt, ObjectId? feedbackReceiverId = null,
DateTime? expiryTime = null, DateTime? timeUsed = null)
{
var list = new List<Token>();
for (var i = 0; i < number; i++)
{
var finalFeedbackReceiverId = ObjectId.GenerateNewId().ToString();
if (feedbackReceiverId != null)
{
finalFeedbackReceiverId = feedbackReceiverId.ToString();
}
list.Add(new Token
{
Id = ObjectId.GenerateNewId().ToString(),
FeedbackReceiverId = finalFeedbackReceiverId,
CreatedAt = createdAt,
TimeUsed = timeUsed,
ExpiryTime = expiryTime
});
}
return list;
}
}
}

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MongoDB.Driver; using MongoDB.Driver;
@ -114,5 +115,91 @@ namespace Retroactiune.Tests.Retroactiune.Core.Services
It.IsAny<FilterDefinition<Token>>(), It.IsAny<FilterDefinition<Token>>(),
It.IsAny<CancellationToken>()), Times.Once); It.IsAny<CancellationToken>()), Times.Once);
} }
[Fact]
public async Task Test_ListTokens_NoFilters_Ok()
{
// Setup
var mongoDatabaseMock = new Mock<IMongoDatabase>();
var mongoClientMock = new Mock<IMongoClient>();
var mongoSettingsMock = new Mock<IDatabaseSettings>();
var mongoCollectionMock = new Mock<IMongoCollection<Token>>();
var mongoCursorMock = new Mock<IAsyncCursor<Token>>();
mongoSettingsMock.SetupGet(i => i.DatabaseName).Returns("MyDB");
mongoSettingsMock.SetupGet(i => i.TokensCollectionName).Returns("tokens");
mongoClientMock
.Setup(stub => stub.GetDatabase(It.IsAny<string>(),
It.IsAny<MongoDatabaseSettings>()))
.Returns(mongoDatabaseMock.Object);
mongoDatabaseMock
.Setup(i => i.GetCollection<Token>(It.IsAny<string>(),
It.IsAny<MongoCollectionSettings>()))
.Returns(mongoCollectionMock.Object);
mongoCollectionMock.Setup(i => i.FindAsync(It.IsAny<FilterDefinition<Token>>(),
It.IsAny<FindOptions<Token, Token>>(), It.IsAny<CancellationToken>())).ReturnsAsync(mongoCursorMock.Object);
// Test
var service = new TokenService(mongoClientMock.Object, mongoSettingsMock.Object);
var result = await service.ListTokens(new TokenListFilters());
// Assert
Assert.IsType<List<Token>>(result);
mongoCollectionMock.Verify(
i
=> i.FindAsync(It.IsAny<FilterDefinition<Token>>(),
It.IsAny<FindOptions<Token, Token>>(),
It.IsAny<CancellationToken>()), Times.Once);
}
[Fact]
public async Task Test_ListTokens_Filters_Ok()
{
// Setup
var mongoDatabaseMock = new Mock<IMongoDatabase>();
var mongoClientMock = new Mock<IMongoClient>();
var mongoSettingsMock = new Mock<IDatabaseSettings>();
var mongoCollectionMock = new Mock<IMongoCollection<Token>>();
var mongoCursorMock = new Mock<IAsyncCursor<Token>>();
mongoSettingsMock.SetupGet(i => i.DatabaseName).Returns("MyDB");
mongoSettingsMock.SetupGet(i => i.TokensCollectionName).Returns("tokens");
mongoClientMock
.Setup(stub => stub.GetDatabase(It.IsAny<string>(),
It.IsAny<MongoDatabaseSettings>()))
.Returns(mongoDatabaseMock.Object);
mongoDatabaseMock
.Setup(i => i.GetCollection<Token>(It.IsAny<string>(),
It.IsAny<MongoCollectionSettings>()))
.Returns(mongoCollectionMock.Object);
mongoCollectionMock.Setup(i => i.FindAsync(It.IsAny<FilterDefinition<Token>>(),
It.IsAny<FindOptions<Token, Token>>(), It.IsAny<CancellationToken>())).ReturnsAsync(mongoCursorMock.Object);
// Test
var service = new TokenService(mongoClientMock.Object, mongoSettingsMock.Object);
var result = await service.ListTokens(new TokenListFilters
{
Ids = new []{"a", "b"},
FeedbackReceiverId = "abc",
CreatedAfter = DateTime.UtcNow,
CreatedBefore = DateTime.UtcNow,
UsedAfter = DateTime.UtcNow,
UsedBefore = DateTime.UtcNow
});
// Assert
Assert.IsType<List<Token>>(result);
mongoCollectionMock.Verify(
i
=> i.FindAsync(It.IsAny<FilterDefinition<Token>>(),
It.IsAny<FindOptions<Token, Token>>(),
It.IsAny<CancellationToken>()), Times.Once);
}
} }
} }

View file

@ -8,6 +8,7 @@ using Moq;
using Retroactiune.Controllers; using Retroactiune.Controllers;
using Retroactiune.Core.Entities; using Retroactiune.Core.Entities;
using Retroactiune.Core.Interfaces; using Retroactiune.Core.Interfaces;
using Retroactiune.Core.Services;
using Retroactiune.DataTransferObjects; using Retroactiune.DataTransferObjects;
using Xunit; using Xunit;
@ -86,6 +87,27 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers
Times.Once); Times.Once);
} }
[Fact]
public async Task DeleteMany_BadRequest()
{
// Arrange
var mapper = TestUtils.GetMapper();
var mockService = new Mock<IFeedbackReceiverService>();
var logger = new Mock<ILogger<FeedbackReceiversController>>();
mockService.Setup(i => i.DeleteManyAsync(It.IsAny<IEnumerable<string>>()))
.ThrowsAsync(new GenericServiceException("op failed"));
// Test
var controller = new FeedbackReceiversController(mockService.Object, mapper, null, logger.Object);
var items = new[] {"bad_guid_but_unit_test_works_cause_validation_doesnt", "2", "3"};
var result = await controller.DeleteMany(items);
// Assert
Assert.IsType<BadRequestObjectResult>(result);
mockService.Verify(s => s.DeleteManyAsync(items),
Times.Once);
}
[Fact] [Fact]
public async Task Get_Successful() public async Task Get_Successful()
{ {

View file

@ -134,5 +134,43 @@ namespace Retroactiune.Tests.Retroactiune.WebAPI.Controllers
Assert.IsType<BadRequestObjectResult>(result); Assert.IsType<BadRequestObjectResult>(result);
tokens.Verify(i => i.DeleteTokens(new[] {"my_guid", "b"}), Times.Once); tokens.Verify(i => i.DeleteTokens(new[] {"my_guid", "b"}), Times.Once);
} }
[Fact]
public async Task Test_ListTokens_Ok()
{
// Arrange
var mapper = TestUtils.GetMapper();
var feedbackService = new Mock<IFeedbackReceiverService>();
var tokens = new Mock<ITokensService>();
var logger = new Mock<ILogger<TokensController>>();
// Test
var controller = new TokensController(feedbackService.Object, tokens.Object, logger.Object, mapper);
var result = await controller.ListTokens(null);
// Assert
Assert.IsType<OkObjectResult>(result);
tokens.Verify(i => i.ListTokens(It.IsAny<TokenListFilters>()), Times.Once);
}
[Fact]
public async Task Test_ListTokens_BadRequest()
{
// Arrange
var mapper = TestUtils.GetMapper();
var feedbackService = new Mock<IFeedbackReceiverService>();
var tokens = new Mock<ITokensService>();
var logger = new Mock<ILogger<TokensController>>();
tokens.Setup(i => i.ListTokens(It.IsAny<TokenListFilters>()))
.Throws(new GenericServiceException("op fail"));
// Test
var controller = new TokensController(feedbackService.Object, tokens.Object, logger.Object, mapper);
var result = await controller.ListTokens(null);
// Assert
Assert.IsType<BadRequestObjectResult>(result);
tokens.Verify(i => i.ListTokens(It.IsAny<TokenListFilters>()), Times.Once);
}
} }
} }

View file

@ -42,7 +42,6 @@ namespace Retroactiune.Controllers
[ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> ListTokens([FromQuery] ListTokensFiltersDto filtersDto) public async Task<IActionResult> ListTokens([FromQuery] ListTokensFiltersDto filtersDto)
{ {
// TODO: Write unit & integration tests.
try try
{ {
var tokenFilters = _mapper.Map<TokenListFilters>(filtersDto); var tokenFilters = _mapper.Map<TokenListFilters>(filtersDto);